From f8d4fa3d161da88e63e51c4a8940a3c44d0ae62e Mon Sep 17 00:00:00 2001
From: Hannah Chow <hannah_chow@brown.edu>
Date: Tue, 9 Apr 2019 22:01:44 -0400
Subject: for now

---
 .../collectionFreeForm/CollectionFreeFormView.tsx         | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1feb30db1..4562fd8a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -206,6 +206,7 @@ export class CollectionFreeFormView extends CollectionSubView {
         const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY));
         this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
         this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
+
     }
 
     @action
@@ -249,10 +250,16 @@ export class CollectionFreeFormView extends CollectionSubView {
     }
 
     focusDocument = (doc: Document) => {
+        this.props.Document.SetText(KeyStore.PanTransformType, "Ease");
         let x = doc.GetNumber(KeyStore.X, 0) + doc.GetNumber(KeyStore.Width, 0) / 2;
         let y = doc.GetNumber(KeyStore.Y, 0) + doc.GetNumber(KeyStore.Height, 0) / 2;
         this.SetPan(x, y);
         this.props.focus(this.props.Document);
+        //     if(this.props.Document.GetText(KeyStore.PanTransformType, "") === "Ease") {
+        //         setTimeout(function(){
+        //              this.props.Document.SetText(KeyStore.PanTransformType, "None");
+        //         }.bind(this),5000);  // wait 5 seconds, then reset to false
+        //    }
     }
 
     getDocumentViewProps(document: Document): DocumentViewProps {
@@ -311,6 +318,12 @@ export class CollectionFreeFormView extends CollectionSubView {
         const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0);
         const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
 
+        if (this.props.Document.GetText(KeyStore.PanTransformType, "") === "Ease") {
+            var freeformclass = "collectionfreeformview-ease";
+        } else {
+            var freeformclass = "collectionfreeformview-none";
+        }
+
         return (
             <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height })}>
                 {({ measureRef }) => (
@@ -324,7 +337,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                                 getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
                                 <PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox}
                                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} >
-                                    <div className="collectionfreeformview" ref={this._canvasRef}
+                                    <div className={freeformclass} ref={this._canvasRef}
                                         style={{ transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}>
                                         {this.backgroundView}
                                         <CollectionFreeFormLinksView {...this.props}>
-- 
cgit v1.2.3-70-g09d2


From 1319573481868f2647259fce37cd15b35c3cafad Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 17 Apr 2019 12:07:55 -0400
Subject: fixed onactivechanged exception.  restored preview region

---
 src/client/util/SelectionManager.ts                           |  4 ++--
 src/client/views/Main.tsx                                     |  2 +-
 src/client/views/MainOverlayTextBox.tsx                       |  2 +-
 src/client/views/collections/CollectionBaseView.tsx           |  8 ++++----
 src/client/views/collections/CollectionDockingView.tsx        |  2 +-
 src/client/views/collections/CollectionSchemaView.tsx         | 11 +++++------
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx                       |  2 +-
 src/client/views/nodes/FieldView.tsx                          |  4 ++--
 src/client/views/nodes/KeyValuePair.tsx                       |  2 +-
 10 files changed, 19 insertions(+), 20 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 320553952..b15a93d9f 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -18,13 +18,13 @@ export namespace SelectionManager {
 
             if (manager.SelectedDocuments.indexOf(doc) === -1) {
                 manager.SelectedDocuments.push(doc);
-                doc.props.onActiveChanged(true);
+                doc.props.whenActiveChanged(true);
             }
         }
 
         @action
         DeselectAll(): void {
-            manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
+            manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false));
             manager.SelectedDocuments = [];
             MainOverlayTextBox.Instance.SetTextDoc();
         }
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 0469211fa..84fdeab92 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -190,7 +190,7 @@ export class Main extends React.Component {
                             selectOnLoad={false}
                             focus={emptyDocFunction}
                             parentActive={returnTrue}
-                            onActiveChanged={emptyFunction}
+                            whenActiveChanged={emptyFunction}
                             ContainingCollectionView={undefined} />}
                 </div>
             }
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 141b3ad74..8cb01117c 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -101,7 +101,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             return <div className="mainOverlayTextBox-textInput" style={{ pointerEvents: "none", transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
                 <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ pointerEvents: "none", transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
-                        selectOnLoad={true} ContainingCollectionView={undefined} onActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
+                        selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ScreenToLocalTransform={() => this._textXf} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 4fda38a26..a8b061b04 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -22,7 +22,7 @@ export interface CollectionRenderProps {
     removeDocument: (document: Document) => boolean;
     moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     active: () => boolean;
-    onActiveChanged: (isActive: boolean) => void;
+    whenActiveChanged: (isActive: boolean) => void;
 }
 
 export interface CollectionViewProps extends FieldViewProps {
@@ -55,9 +55,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     //TODO should this be observable?
     private _isChildActive = false;
-    onActiveChanged = (isActive: boolean) => {
+    whenActiveChanged = (isActive: boolean) => {
         this._isChildActive = isActive;
-        this.props.onActiveChanged(isActive);
+        this.props.whenActiveChanged(isActive);
     }
 
     createsCycle(documentToAdd: Document, containerDocument: Document): boolean {
@@ -184,7 +184,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             removeDocument: this.removeDocument,
             moveDocument: this.moveDocument,
             active: this.active,
-            onActiveChanged: this.onActiveChanged,
+            whenActiveChanged: this.whenActiveChanged,
         };
         const viewtype = this.collectionViewType;
         return (
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2b886adb6..9ec0b3aeb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -363,7 +363,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     isTopMost={true}
                     selectOnLoad={false}
                     parentActive={returnTrue}
-                    onActiveChanged={emptyFunction}
+                    whenActiveChanged={emptyFunction}
                     focus={emptyDocFunction}
                     ContainingCollectionView={undefined} />
             </div>;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1defdba7e..15b7617ae 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -24,7 +24,7 @@ import { EditableView } from "../EditableView";
 import { DocumentView } from "../nodes/DocumentView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./CollectionSchemaView.scss";
-import { CollectionSubView } from "./CollectionSubView";
+import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 
 
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
@@ -70,7 +70,6 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
 
-
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
             Document: rowProps.value[0],
@@ -83,7 +82,7 @@ export class CollectionSchemaView extends CollectionSubView {
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyDocFunction,
             active: returnFalse,
-            onActiveChanged: emptyFunction,
+            whenActiveChanged: emptyFunction,
         };
         let contents = (
             <FieldView {...props} />
@@ -285,7 +284,7 @@ export class CollectionSchemaView extends CollectionSubView {
         this._optionsActivated++;
     }
 
-    @observable previewScript: string = "this";
+    @observable previewScript: string = "";
     @action
     onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.previewScript = e.currentTarget.value;
@@ -299,7 +298,7 @@ export class CollectionSchemaView extends CollectionSubView {
         const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined;
         //all the keys/columns that will be displayed in the schema
         const allKeys = this.findAllDocumentKeys;
-        let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined;
+        let doc: any = selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) : selected) : undefined;
 
         // let doc = CompileScript(this.previewScript, { this: selected }, true)();
         let content = this._selectedIndex === -1 || !selected ? (null) : (
@@ -318,7 +317,7 @@ export class CollectionSchemaView extends CollectionSubView {
                                 ContainingCollectionView={this.props.CollectionView}
                                 focus={emptyDocFunction}
                                 parentActive={this.props.active}
-                                onActiveChanged={this.props.onActiveChanged} /> : null}
+                                whenActiveChanged={this.props.whenActiveChanged} /> : null}
                         <input value={this.previewScript} onChange={this.onPreviewScriptChange}
                             style={{ position: 'absolute', bottom: '0px' }} />
                     </div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 97708ce19..80322c900 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -248,7 +248,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
             parentActive: this.props.active,
-            onActiveChanged: this.props.active,
+            whenActiveChanged: this.props.active,
         };
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b99e449be..d74f9fc57 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -39,7 +39,7 @@ export interface DocumentViewProps {
     focus: (doc: Document) => void;
     selectOnLoad: boolean;
     parentActive: () => boolean;
-    onActiveChanged: (isActive: boolean) => void;
+    whenActiveChanged: (isActive: boolean) => void;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ebd25f937..e9f6950ff 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -43,7 +43,7 @@ export interface FieldViewProps {
     moveDocument?: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     active: () => boolean;
-    onActiveChanged: (isActive: boolean) => void;
+    whenActiveChanged: (isActive: boolean) => void;
     focus: (doc: Document) => void;
 }
 
@@ -95,7 +95,7 @@ export class FieldView extends React.Component<FieldViewProps> {
                     layoutKey={KeyStore.Layout}
                     ContainingCollectionView={this.props.ContainingCollectionView}
                     parentActive={this.props.active}
-                    onActiveChanged={this.props.onActiveChanged} />
+                    whenActiveChanged={this.props.whenActiveChanged} />
             );
         }
         else if (field instanceof ListField) {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 5d69f23b2..d480eb5af 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -48,7 +48,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             isTopMost: false,
             selectOnLoad: false,
             active: returnFalse,
-            onActiveChanged: emptyFunction,
+            whenActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyDocFunction,
         };
-- 
cgit v1.2.3-70-g09d2


From 582e9f5ed1da107f0fb0ae3c0e15027f79d4187a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Apr 2019 21:50:33 -0400
Subject: fixed docking view and freeform to not need <Measure>

---
 .../views/collections/CollectionDockingView.scss   |  4 --
 .../views/collections/CollectionDockingView.tsx    | 49 ++++++++++-----------
 .../collectionFreeForm/CollectionFreeFormView.scss |  9 +---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 50 +++++++++-------------
 4 files changed, 44 insertions(+), 68 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index b4d9efc47..0e7e0afa7 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -2,10 +2,6 @@
 
 .collectiondockingview-content {
     height: 100%;
-    text-align:center;
-}
-.collectiondockingview-content-height {
-    height: 100%;
 }
 .lm_active .messageCounter{
     color:white;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 62321b7a8..39e27b601 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -316,7 +316,7 @@ interface DockedFrameProps {
 @observer
 export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
-    private _mainCont = React.createRef<HTMLDivElement>();
+    _mainCont = React.createRef<HTMLDivElement>();
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
     @observable private _document: Opt<Document>;
@@ -326,38 +326,32 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document));
     }
 
-    private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
-    private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
-    private _contentScaling = () => {
-        let wscale = this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth);
-        if (wscale * this._nativeHeight() > this._panelHeight)
-            return this._panelHeight / (this._nativeHeight() ? this._nativeHeight() : this._panelHeight);
+    nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth);
+    nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight);
+    contentScaling = () => {
+        let wscale = this._panelWidth / (this.nativeWidth() ? this.nativeWidth() : this._panelWidth);
+        if (wscale * this.nativeHeight() > this._panelHeight)
+            return this._panelHeight / (this.nativeHeight() ? this.nativeHeight() : this._panelHeight);
         return wscale;
     }
 
     ScreenToLocalTransform = () => {
         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 CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling());
     }
-    get previewPanelCenteringOffset() { return (this._panelWidth - this._nativeWidth() * this._contentScaling()) / 2; }
+    get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
 
-
-    render() {
-        if (!this._document) {
-            return (null);
-        }
-        let wscale = this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth);
-        let name = (wscale * this._nativeHeight() > this._panelHeight) ? "" : "-height";
-        var content =
-            <div className={`collectionDockingView-content${name}`} ref={this._mainCont}
+    get content() {
+        return (
+            <div className="collectionDockingView-content" ref={this._mainCont}
                 style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
-                <DocumentView key={this._document.Id} Document={this._document}
+                <DocumentView key={this._document!.Id} Document={this._document!}
                     addDocument={undefined}
                     removeDocument={undefined}
-                    ContentScaling={this._contentScaling}
-                    PanelWidth={this._nativeWidth}
-                    PanelHeight={this._nativeHeight}
+                    ContentScaling={this.contentScaling}
+                    PanelWidth={this.nativeWidth}
+                    PanelHeight={this.nativeHeight}
                     ScreenToLocalTransform={this.ScreenToLocalTransform}
                     isTopMost={true}
                     selectOnLoad={false}
@@ -365,10 +359,13 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                     whenActiveChanged={emptyFunction}
                     focus={emptyDocFunction}
                     ContainingCollectionView={undefined} />
-            </div>;
+            </div>);
+    }
 
-        return <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
-            {({ measureRef }) => <div ref={measureRef}>  {content} </div>}
-        </Measure>;
+    render() {
+        return !this._document ? (null) :
+            <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}>
+                {({ measureRef }) => <div ref={measureRef}>  {this.content} </div>}
+            </Measure>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 392bd514f..57706b51e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,11 +1,4 @@
 @import "../../globalCssVariables";
-.collectionfreeformview-measure {
-    position: inherit;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-  }
 .collectionfreeformview {
   position: inherit;
   top: 0;
@@ -52,7 +45,7 @@
   }
  
   opacity: 0.99;
-  border-width: 0;
+  border-width: 0; 
   border-color: transparent;
   border-style: solid; 
   border-radius: $border-radius;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 80322c900..d4385b12c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -28,8 +28,8 @@ export class CollectionFreeFormView extends CollectionSubView {
     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;
-    @observable private _pwidth: number = 0;
-    @observable private _pheight: 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 nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
@@ -268,11 +268,6 @@ export class CollectionFreeFormView extends CollectionSubView {
         return docviews;
     }
 
-    @action
-    onResize = (r: any) => {
-        this._pwidth = r.entry.width;
-        this._pheight = r.entry.height;
-    }
     @action
     onCursorMove = (e: React.PointerEvent) => {
         super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
@@ -281,29 +276,24 @@ export class CollectionFreeFormView extends CollectionSubView {
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
-            <Measure onResize={this.onResize}>
-                {({ measureRef }) => (
-                    <div className="collectionfreeformview-measure" ref={measureRef}>
-                        <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
-                            onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
-                            <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 centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
-                                    zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
-                                    <CollectionFreeFormBackgroundView {...this.getDocumentViewProps(this.props.Document)} />
-                                    <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
-                                        <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
-                                            {this.childViews}
-                                        </InkingCanvas>
-                                    </CollectionFreeFormLinksView>
-                                    <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
-                                </CollectionFreeFormViewPannableContents>
-                                <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
-                            </MarqueeView>
-                        </div>
-                    </div>)}
-            </Measure>
+            <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
+                onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} >
+                <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 centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
+                        zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
+                        <CollectionFreeFormBackgroundView {...this.getDocumentViewProps(this.props.Document)} />
+                        <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
+                            <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
+                                {this.childViews}
+                            </InkingCanvas>
+                        </CollectionFreeFormLinksView>
+                        <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+                    </CollectionFreeFormViewPannableContents>
+                    <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
+                </MarqueeView>
+            </div>
         );
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 80a58ec7213d3c7cd1a290a1dd291891e8333803 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Apr 2019 22:33:03 -0400
Subject: fixed initial heights of videso and images

---
 src/client/views/Main.tsx                                         | 4 ++--
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx     | 4 +++-
 src/client/views/nodes/ImageBox.tsx                               | 8 ++++++--
 3 files changed, 11 insertions(+), 5 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 84fdeab92..5cae4fdaf 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -210,9 +210,9 @@ export class Main extends React.Component {
         let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
         let addSchemaNode = action(() => Documents.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
         let addTreeNode = action(() => Documents.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true }));
-        let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, height: 200, title: "video node" }));
+        let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, title: "video node" }));
         let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" }));
-        let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, height: 200, title: "an image of a cat" }));
+        let addImageNode = action(() => Documents.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
         let addWebNode = action(() => Documents.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" }));
         let addAudioNode = action(() => Documents.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" }));
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d4385b12c..228fc937e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -85,7 +85,9 @@ export class CollectionFreeFormView extends CollectionSubView {
                         d.SetNumber(KeyStore.Width, 300);
                     }
                     if (!d.GetNumber(KeyStore.Height, 0)) {
-                        d.SetNumber(KeyStore.Height, 300);
+                        let nw = d.GetNumber(KeyStore.NativeWidth, 0);
+                        let nh = d.GetNumber(KeyStore.NativeHeight, 0);
+                        d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
                     }
                     this.bringToFront(d);
                 });
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index fe0b07bc0..38a588812 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -38,7 +38,10 @@ export class ImageBox extends React.Component<FieldViewProps> {
     onLoad = (target: any) => {
         var h = this._imgRef.current!.naturalHeight;
         var w = this._imgRef.current!.naturalWidth;
-        if (this._photoIndex == 0) this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
+        if (this._photoIndex == 0) {
+            this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
+            this.props.Document.SetNumber(KeyStore.Height, this.props.Document.Width() * h / w);
+        }
     }
 
 
@@ -65,7 +68,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
                 if (layout.indexOf(ImageBox.name) !== -1) {
                     let imgData = this.props.Document.Get(KeyStore.Data);
                     if (imgData instanceof ImageField && imgData) {
-                        this.props.Document.Set(KeyStore.Data, new ListField([imgData]));
+                        this.props.Document.SetOnPrototype(KeyStore.Data, new ListField([imgData]));
                     }
                     let imgList = this.props.Document.GetList(KeyStore.Data, [] as any[]);
                     if (imgList) {
@@ -137,6 +140,7 @@ export class ImageBox extends React.Component<FieldViewProps> {
     @action
     onDotDown(index: number) {
         this._photoIndex = index;
+        this.props.Document.SetNumber(KeyStore.CurPage, index);
     }
 
     dots(paths: string[]) {
-- 
cgit v1.2.3-70-g09d2


From 0bd545674070fe90836912c7171642cf09e82696 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Apr 2019 11:21:58 -0400
Subject: reenabled zoon fading.  added Esc.

---
 src/client/util/DragManager.ts                       |  6 ++++--
 src/client/util/SelectionManager.ts                  |  1 +
 src/client/views/Main.tsx                            | 17 ++++++++++++-----
 src/client/views/collections/CollectionBaseView.tsx  |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx    | 11 +++++------
 .../views/nodes/CollectionFreeFormDocumentView.tsx   | 20 ++++++++++----------
 src/client/views/nodes/FormattedTextBox.tsx          |  4 ++++
 7 files changed, 37 insertions(+), 24 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4bd654e15..64ea4342e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -269,20 +269,22 @@ export namespace DragManager {
             );
         };
 
-        const abortDrag = () => {
+        AbortDrag = () => {
             document.removeEventListener("pointermove", moveHandler, true);
             document.removeEventListener("pointerup", upHandler);
             dragElements.map(dragElement => dragDiv.removeChild(dragElement));
             eles.map(ele => (ele.hidden = false));
         };
         const upHandler = (e: PointerEvent) => {
-            abortDrag();
+            AbortDrag();
             FinishDrag(eles, e, dragData, options, finishDrag);
         };
         document.addEventListener("pointermove", moveHandler, true);
         document.addEventListener("pointerup", upHandler);
     }
 
+    export let AbortDrag: () => void = emptyFunction;
+
     function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
         let removed = dragEles.map(dragEle => {
             let parent = dragEle.parentElement;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index b15a93d9f..20319dff9 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -3,6 +3,7 @@ import { DocumentView } from "../views/nodes/DocumentView";
 import { Document } from "../../fields/Document";
 import { Main } from "../views/Main";
 import { MainOverlayTextBox } from "../views/MainOverlayTextBox";
+import { DragManager } from "./DragManager";
 
 export namespace SelectionManager {
     class Manager {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 5cae4fdaf..503a11b35 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -27,7 +27,7 @@ import '../northstar/model/ModelExtensions';
 import { HistogramOperation } from '../northstar/operations/HistogramOperation';
 import '../northstar/utils/Extensions';
 import { Server } from '../Server';
-import { SetupDrag } from '../util/DragManager';
+import { SetupDrag, DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -38,6 +38,7 @@ import "./Main.scss";
 import { MainOverlayTextBox } from './MainOverlayTextBox';
 import { DocumentView } from './nodes/DocumentView';
 import { PreviewCursor } from './PreviewCursor';
+import { SelectionManager } from '../util/SelectionManager';
 
 
 @observer
@@ -84,11 +85,11 @@ export class Main extends React.Component {
         this.initEventListeners();
         this.initAuthenticationRouters();
 
-        try {
-            this.initializeNorthstar();
-        } catch (e) {
+        // try {
+        //     this.initializeNorthstar();
+        // } catch (e) {
 
-        }
+        // }
     }
 
     componentDidMount() { window.onpopstate = this.onHistory; }
@@ -111,6 +112,12 @@ export class Main extends React.Component {
         // window.addEventListener("pointermove", (e) => this.reportLocation(e))
         window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
         window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
+        window.addEventListener("keydown", (e) => {
+            if (e.key == "Escape") {
+                DragManager.AbortDrag();
+                SelectionManager.DeselectAll()
+            }
+        }, false); // drag event handler
         // click interactions for the context menu
         document.addEventListener("pointerdown", action(function (e: PointerEvent) {
             if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index a8b061b04..4755b2d57 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -90,7 +90,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         let props = this.props;
         var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
         doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
-        if (this.isAnnotationOverlay) {
+        if (true || this.isAnnotationOverlay) {
             doc.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
         }
         if (curPage >= 0) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 228fc937e..159db6279 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,5 @@
 import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
-import Measure from "react-measure";
 import { Document } from "../../../../fields/Document";
 import { KeyStore } from "../../../../fields/KeyStore";
 import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
@@ -49,9 +48,7 @@ export class CollectionFreeFormView extends CollectionSubView {
         this.addDocument(newBox, false);
     }
     private addDocument = (newBox: Document, allowDuplicates: boolean) => {
-        if (this.isAnnotationOverlay) {
-            newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
-        }
+        newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
         return this.props.addDocument(this.bringToFront(newBox), false);
     }
     private selectDocuments = (docs: Document[]) => {
@@ -187,14 +184,16 @@ export class CollectionFreeFormView extends CollectionSubView {
             // if (modes[e.deltaMode] === 'pixels') coefficient = 50;
             // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height??
             let deltaScale = (1 - (e.deltaY / coefficient));
+            if (deltaScale < 0) deltaScale = -deltaScale;
             if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
                 deltaScale = 1 / this.zoomScaling();
             }
             let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
             let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-            this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
-            this.setPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+            let safeScale = Math.abs(localTransform.Scale);
+            this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale));
+            this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
             e.stopPropagation();
         }
     }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8cf7a0dd2..97d53a47e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -66,22 +66,22 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
 
     render() {
         let zoomFade = 1;
-        // //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
-        // let transform = (this.props.ScreenToLocalTransform().scale(this.props.ContentScaling())).inverse();
-        // var [sptX, sptY] = transform.transformPoint(0, 0);
-        // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-        // let w = bptX - sptX;
-        // //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
-        // let fadeUp = .75 * 1800;
-        // let fadeDown = .075 * 1800;
-        // zoomFade = w < fadeDown || w > fadeUp ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+        //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
+        let transform = this.getTransform().scale(this.contentScaling()).inverse();
+        var [sptX, sptY] = transform.transformPoint(0, 0);
+        let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
+        let w = bptX - sptX;
+        //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+        let fadeUp = .75 * 1800;
+        let fadeDown = .075 * 1800;
+        zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
 
         return (
             <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
                 opacity: zoomFade,
                 transformOrigin: "left top",
                 transform: this.transform,
-                pointerEvents: "all",
+                pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
                 width: this.width,
                 height: this.height,
                 position: "absolute",
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index bff8ca7a4..639dae30a 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -19,6 +19,7 @@ import { MainOverlayTextBox } from "../MainOverlayTextBox";
 import { FieldView, FieldViewProps } from "./FieldView";
 import "./FormattedTextBox.scss";
 import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -251,6 +252,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     }
 
     onKeyPress(e: React.KeyboardEvent) {
+        if (e.key == "Escape") {
+            SelectionManager.DeselectAll();
+        }
         e.stopPropagation();
         if (e.keyCode === 9) e.preventDefault();
         // stop propagation doesn't seem to stop propagation of native keyboard events.
-- 
cgit v1.2.3-70-g09d2


From 95dff75290edc9b13e9741eefe2bc5851a60dde6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Apr 2019 13:51:25 -0400
Subject: fixed workspace switching exception.  started to refresh minimize
 functionality.

---
 src/client/util/DragManager.ts                     |  2 +-
 .../views/collections/CollectionDockingView.tsx    | 11 +++++----
 .../views/collections/CollectionTreeView.tsx       |  9 +++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 23 +++++++++++--------
 .../collections/collectionFreeForm/MarqueeView.tsx | 10 +++++----
 src/client/views/globalCssVariables.scss           |  2 ++
 src/client/views/globalCssVariables.scss.d.ts      |  1 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 16 +++++++++----
 src/client/views/nodes/DocumentView.scss           | 13 +++++------
 src/client/views/nodes/DocumentView.tsx            | 26 +++++++++++++++++-----
 src/fields/KeyStore.ts                             |  4 +++-
 11 files changed, 76 insertions(+), 41 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 64ea4342e..426c9fc3d 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -250,7 +250,7 @@ export namespace DragManager {
                 dragData.aliasOnDrop = e.ctrlKey || e.altKey;
             }
             if (e.shiftKey) {
-                abortDrag();
+                AbortDrag();
                 CollectionDockingView.Instance.StartOtherDrag(docs, {
                     pageX: e.pageX,
                     pageY: e.pageY,
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 39e27b601..e4c647635 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -19,7 +19,7 @@ import { ServerUtils } from "../../../server/ServerUtil";
 import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
 import { TextField } from "../../../fields/TextField";
 import { ListField } from "../../../fields/ListField";
-import { thisExpression } from "babel-types";
+import { Transform } from '../../util/Transform'
 
 @observer
 export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@@ -336,9 +336,12 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     }
 
     ScreenToLocalTransform = () => {
-        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());
+        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;
+            return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling());
+        }
+        return Transform.Identity();
     }
     get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 51a02fc25..e0387f4b4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,20 +1,17 @@
 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, trace } from "mobx";
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { FieldWaiting } from "../../../fields/Field";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { SetupDrag, DragManager } from "../../util/DragManager";
+import { DragManager, SetupDrag } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
-import "./CollectionTreeView.scss";
-import { CollectionView } from "./CollectionView";
-import * as globalCssVariables from "../../views/globalCssVariables.scss";
 import { CollectionSubView } from "./CollectionSubView";
+import "./CollectionTreeView.scss";
 import React = require("react");
-import { props } from 'bluebird';
 
 
 export interface TreeViewProps {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 159db6279..af6819cd4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -71,20 +71,25 @@ export class CollectionFreeFormView extends CollectionSubView {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
+            console.log("DROP Aat " + de.x + " off " + de.data.xOffset);
             const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
-                let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0);
-                let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0);
+                let dragDoc = de.data.droppedDocuments[0];
+                let dropX = dragDoc.GetNumber(KeyStore.X, 0);
+                let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
                 de.data.droppedDocuments.map(d => {
+                    let minimized = d.GetBoolean(KeyStore.Minimized, false);
                     d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
                     d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
-                    if (!d.GetNumber(KeyStore.Width, 0)) {
-                        d.SetNumber(KeyStore.Width, 300);
-                    }
-                    if (!d.GetNumber(KeyStore.Height, 0)) {
-                        let nw = d.GetNumber(KeyStore.NativeWidth, 0);
-                        let nh = d.GetNumber(KeyStore.NativeHeight, 0);
-                        d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
+                    if (!minimized) {
+                        if (!d.GetNumber(KeyStore.Width, 0)) {
+                            d.SetNumber(KeyStore.Width, 300);
+                        }
+                        if (!d.GetNumber(KeyStore.Height, 0)) {
+                            let nw = d.GetNumber(KeyStore.NativeWidth, 0);
+                            let nh = d.GetNumber(KeyStore.NativeHeight, 0);
+                            d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
+                        }
                     }
                     this.bringToFront(d);
                 });
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 65f461b27..8b94374fa 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -11,6 +11,7 @@ import { undoBatch } from "../../../util/UndoManager";
 import { InkingCanvas } from "../../InkingCanvas";
 import { PreviewCursor } from "../../PreviewCursor";
 import { CollectionFreeFormView } from "./CollectionFreeFormView";
+import { MINIMIZED_ICON_SIZE } from '../../../views/globalCssVariables.scss'
 import "./MarqueeView.scss";
 import React = require("react");
 
@@ -207,11 +208,12 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let selRect = this.Bounds;
         let selection: Document[] = [];
         this.props.activeDocuments().map(doc => {
+            let minimized = doc.GetBoolean(KeyStore.Minimized, false);
             var z = doc.GetNumber(KeyStore.Zoom, 1);
-            var x = doc.GetNumber(KeyStore.X, 0);
-            var y = doc.GetNumber(KeyStore.Y, 0);
-            var w = doc.Width() / z;
-            var h = doc.Height() / z;
+            var x = doc.GetNumber(KeyStore.X, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedX, 0) : 0);
+            var y = doc.GetNumber(KeyStore.Y, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedY, 0) : 0);
+            var w = minimized ? MINIMIZED_ICON_SIZE : doc.Width() / z;
+            var h = minimized ? MINIMIZED_ICON_SIZE : doc.Height() / z;
             if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
                 selection.push(doc);
             }
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 5c8e9c8fc..f154f8158 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -23,7 +23,9 @@ $mainTextInput-zindex: 999; // then text input overlay so that it's context menu
 $docDecorations-zindex: 998; // then doc decorations appear over everything else
 $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
 $COLLECTION_BORDER_WIDTH: 1;
+$MINIMIZED_ICON_SIZE:25;
 :export  {
     contextMenuZindex: $contextMenu-zindex;
     COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH;
+    MINIMIZED_ICON_SIZE: $MINIMIZED_ICON_SIZE;
 }
\ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index ed8573f41..cc77d987a 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -2,6 +2,7 @@
 interface IGlobalScss {
     contextMenuZindex: string;  // context menu shows up over everything
     COLLECTION_BORDER_WIDTH: string;
+    MINIMIZED_ICON_SIZE: string;
 }
 declare const globalCssVariables: IGlobalScss;
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 97d53a47e..a3689414d 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -17,7 +17,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
 
     @computed
     get transform(): string {
-        return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.props.Document.GetNumber(KeyStore.X, 0)}px, ${this.props.Document.GetNumber(KeyStore.Y, 0)}px) scale(${this.zoom}, ${this.zoom}) `;
+        return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
     }
 
     @computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.Zoom, 1); }
@@ -45,14 +45,20 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         this.props.Document.SetData(KeyStore.ZIndex, h, NumberField);
     }
 
+    get X() {
+        return this.props.Document.GetNumber(KeyStore.X, 0) + (this.isMinimized ? this.props.Document.GetNumber(KeyStore.MinimizedX, 0) : 0);
+    }
+    get Y() {
+        return this.props.Document.GetNumber(KeyStore.Y, 0) + (this.isMinimized ? this.props.Document.GetNumber(KeyStore.MinimizedY, 0) : 0);
+    }
     getTransform = (): Transform =>
         this.props.ScreenToLocalTransform()
-            .translate(-this.props.Document.GetNumber(KeyStore.X, 0), -this.props.Document.GetNumber(KeyStore.Y, 0))
+            .translate(-this.X, -this.Y)
             .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
-    panelWidth = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelWidth();
-    panelHeight = () => this.props.Document.GetBoolean(KeyStore.Minimized, false) ? 10 : this.props.PanelHeight();
+    panelWidth = () => this.isMinimized ? 10 : this.props.PanelWidth();
+    panelHeight = () => this.isMinimized ? 10 : this.props.PanelHeight();
 
     @computed
     get docView() {
@@ -64,6 +70,8 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         />;
     }
 
+    get isMinimized() { return this.props.Document.GetBoolean(KeyStore.Minimized, false); }
+
     render() {
         let zoomFade = 1;
         //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 690ee50e8..85c305b5a 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -13,7 +13,6 @@
   }
 
   .top {
-    background: #232323;
     height: 20px;
     cursor: pointer;
   }
@@ -32,13 +31,13 @@
 .documentView-node-topmost {
     background: white;
 }
-
 .minimized-box {
-  height: 10px;
-  width: 10px;
-  border-radius: 2px;
-  background: $dark-color;
-  transform-origin: left top;
+    position: absolute;
+    left:0;
+    top:0;
+    width:$MINIMIZED_ICON_SIZE;
+    height:$MINIMIZED_ICON_SIZE;
+    transform-origin: left top;
 }
 
 .minimized-box:hover {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d74f9fc57..af52e44e1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,12 +1,13 @@
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faCaretUp, faObjectGroup, faStickyNote, faFilePdf, faFilm, faImage } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { BooleanField } from "../../../fields/BooleanField";
 import { Document } from "../../../fields/Document";
-import { Field, FieldWaiting, Opt } from "../../../fields/Field";
+import { Field, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { TextField } from "../../../fields/TextField";
 import { ServerUtils } from "../../../server/ServerUtil";
 import { emptyFunction, Utils } from "../../../Utils";
 import { Documents } from "../../documents/Documents";
@@ -19,12 +20,19 @@ import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
+import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
 import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
 
 
+library.add(faCaretUp);
+library.add(faObjectGroup);
+library.add(faStickyNote);
+library.add(faFilePdf);
+library.add(faFilm);
+
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
     Document: Document;
@@ -216,6 +224,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @action
     public minimize = (): void => {
         this.props.Document.SetBoolean(KeyStore.Minimized, true);
+        this.props.Document.SetNumber(KeyStore.MinimizedX, 0);
+        this.props.Document.SetNumber(KeyStore.MinimizedY, 0);
         SelectionManager.DeselectAll();
     }
 
@@ -314,8 +324,14 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         var nativeWidth = this.nativeWidth > 0 ? this.nativeWidth.toString() + "px" : "100%";
 
         if (this.isMinimized()) {
-            return <div className="minimized-box" ref={this._mainCont} onClick={this.expand} onDrop={this.onDrop}
-                style={{ transform: `scale(${scaling} , ${scaling})` }} onPointerDown={this.onPointerDown} />;
+            let button = this.layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+                this.layout.indexOf("ImageBox") !== -1 ? faImage :
+                    this.layout.indexOf("Formatted") !== -1 ? faStickyNote :
+                        this.layout.indexOf("Collection") !== -1 ? faObjectGroup :
+                            faCaretUp;
+            return <div className="minimized-box" ref={this._mainCont} onClick={this.expand} onDrop={this.onDrop} onPointerDown={this.onPointerDown} >
+                <FontAwesomeIcon icon={button} style={{ width: MINIMIZED_ICON_SIZE, height: MINIMIZED_ICON_SIZE }} className="documentView-minimizedIcon" />
+            </div>
         }
         return (
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 16a909eb8..ff2f31003 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -4,6 +4,8 @@ export namespace KeyStore {
     export const Prototype = new Key("Prototype");
     export const X = new Key("X");
     export const Y = new Key("Y");
+    export const MinimizedX = new Key("MinimizedX");
+    export const MinimizedY = new Key("MinimizedY");
     export const Page = new Key("Page");
     export const Title = new Key("Title");
     export const Author = new Key("Author");
@@ -48,7 +50,7 @@ export namespace KeyStore {
     export const Minimized = new Key("Minimized");
     export const CopyDraggedItems = new Key("CopyDraggedItems");
 
-    export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
+    export const KeyList: Key[] = [Prototype, X, Y, MinimizedX, MinimizedY, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
         Width, Height, ZIndex, Zoom, 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,
-- 
cgit v1.2.3-70-g09d2


From 80c7baeaa82697428730a24382f660d82ca209b6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Apr 2019 15:57:25 -0400
Subject: icons

---
 src/client/views/DocumentDecorations.scss          |  4 +--
 src/client/views/DocumentDecorations.tsx           | 41 ++++++++++++++++++++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +--
 src/client/views/nodes/DocumentView.scss           |  4 +--
 src/client/views/nodes/DocumentView.tsx            | 35 ++++++++++--------
 5 files changed, 65 insertions(+), 23 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index c1a949639..8af8a1184 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -3,12 +3,12 @@
 .documentDecorations {
     position: absolute;
 }
-#documentDecorations-container {
+.documentDecorations-container {
+    z-index: $docDecorations-zindex;
     position: absolute;
     top: 0;
     left:0;
     display: grid;
-    z-index: $docDecorations-zindex;
     grid-template-rows: 20px 8px 1fr 8px;
     grid-template-columns: 8px 8px 1fr 8px 8px;
     pointer-events: none;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 16fac0694..6da1ea52e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -11,6 +11,7 @@ import { SelectionManager } from "../util/SelectionManager";
 import { undoBatch } from "../util/UndoManager";
 import './DocumentDecorations.scss';
 import { MainOverlayTextBox } from "./MainOverlayTextBox";
+import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
@@ -36,6 +37,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @observable private _hidden = false;
     @observable private _opacity = 1;
     @observable private _dragging = false;
+    @observable private _iconifying: number[] = [];
 
 
     constructor(props: Readonly<{}>) {
@@ -175,25 +177,46 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             document.removeEventListener("pointerup", this.onCloseUp);
         }
     }
+    _minimizeDownX = 0;
+    _minimizeDownY = 0;
+    _minimizeDownOffX = 0;
+    _minimizeDownOffY = 0;
     onMinimizeDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
         if (e.button === 0) {
+            this._minimizeDownX = e.clientX;
+            this._minimizeDownY = e.clientY;
+            let xf = SelectionManager.SelectedDocuments()[0].props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+            this._minimizeDownOffX = xf[0] - e.clientX;
+            this._minimizeDownOffY = xf[1] - e.clientY;
             document.removeEventListener("pointermove", this.onMinimizeMove);
             document.addEventListener("pointermove", this.onMinimizeMove);
             document.removeEventListener("pointerup", this.onMinimizeUp);
             document.addEventListener("pointerup", this.onMinimizeUp);
         }
     }
+    @action
     onMinimizeMove = (e: PointerEvent): void => {
         e.stopPropagation();
+        let dx = e.clientX - this._minimizeDownX;
+        let dy = e.clientY - this._minimizeDownY;
+        if (Math.abs(dx) > 4 || Math.abs(dy) > 4)
+            this._iconifying = [e.pageX - Number(MINIMIZED_ICON_SIZE) / 2 + 7, e.pageY - Number(MINIMIZED_ICON_SIZE) / 2 + 4];
     }
+    @action
     onMinimizeUp = (e: PointerEvent): void => {
         e.stopPropagation();
         if (e.button === 0) {
-            SelectionManager.SelectedDocuments().map(dv => dv.minimize());
+            let dx = e.clientX - this._minimizeDownX - this._minimizeDownOffX;
+            let dy = e.clientY - this._minimizeDownY - this._minimizeDownOffY;
+            if (Math.abs(dx + this._minimizeDownOffX) < 4 && Math.abs(dy + this._minimizeDownOffY) < 4 && this._iconifying.length == 0)
+                dx = dy = 0;
+            SelectionManager.SelectedDocuments().map(dv => dv.minimize((dv.props.ScreenToLocalTransform()).scale(dv.props.ContentScaling()).transformDirection(dx, dy)));
             document.removeEventListener("pointermove", this.onMinimizeMove);
             document.removeEventListener("pointerup", this.onMinimizeUp);
+            SelectionManager.DeselectAll();
         }
+        this._iconifying = [];
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
@@ -373,6 +396,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (bounds.x === Number.MAX_VALUE) {
             return (null);
         }
+        let xf = this._iconifying.length ? `translate(${this._iconifying[0]}px, ${this._iconifying[1]}px)` : "translate(0,0)";
+        let minimizeIcon = (
+            <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown} style={{ transform: xf }}>
+                <div style={{ position: "absolute", left: "-7px", top: "-4px", width: `${MINIMIZED_ICON_SIZE}`, height: `${MINIMIZED_ICON_SIZE}` }}>
+                    {SelectionManager.SelectedDocuments().length == 1 ? SelectionManager.SelectedDocuments()[0].minimizedIcon : "..."}
+                </div>
+            </div>);
+        if (this._iconifying.length) {
+            return <div className="documentDecorations-container">{minimizeIcon}</div>;
+        }
         // console.log(this._documents.length)
         // let test = this._documents[0].props.Document.Title;
         if (this.Hidden) {
@@ -383,6 +416,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             return (null);
         }
 
+
         let linkButton = null;
         if (SelectionManager.SelectedDocuments().length > 0) {
             let selFirst = SelectionManager.SelectedDocuments()[0];
@@ -406,14 +440,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
             }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
             </div>
-            <div id="documentDecorations-container" style={{
+            <div className="documentDecorations-container" style={{
                 width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
                 height: (bounds.b - bounds.y + this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight) + "px",
                 left: bounds.x - this._resizeBorderWidth / 2,
                 top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 opacity: this._opacity
             }}>
-                <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}>...</div>
+                {minimizeIcon}
+
                 <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this.getValue()} onChange={this.handleChange} onPointerDown={this.onBackgroundDown} onKeyPress={this.enterPressed} />
                 <div className="documentDecorations-closeButton" onPointerDown={this.onCloseDown}>X</div>
                 <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index af6819cd4..c50597655 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -79,8 +79,8 @@ export class CollectionFreeFormView extends CollectionSubView {
                 let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
                 de.data.droppedDocuments.map(d => {
                     let minimized = d.GetBoolean(KeyStore.Minimized, false);
-                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
-                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
+                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - (minimized ? d.GetNumber(KeyStore.MinimizedX, 0) : 0)) - dropX);
+                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - (minimized ? d.GetNumber(KeyStore.MinimizedY, 0) : 0)) - dropY);
                     if (!minimized) {
                         if (!d.GetNumber(KeyStore.Width, 0)) {
                             d.SetNumber(KeyStore.Width, 300);
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 85c305b5a..a0e829d26 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -37,11 +37,11 @@
     top:0;
     width:$MINIMIZED_ICON_SIZE;
     height:$MINIMIZED_ICON_SIZE;
-    transform-origin: left top;
+    transform:translate(-50%, -50%);
 }
 
 .minimized-box:hover {
   background: $main-accent;
-  transform: scale(1.15);
+  transform:translate(-50%, -50%) scale(1.15);;
   cursor: pointer;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index af52e44e1..3ada3252c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -222,11 +222,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     @action
-    public minimize = (): void => {
+    public minimize = (where: number[]): void => {
         this.props.Document.SetBoolean(KeyStore.Minimized, true);
-        this.props.Document.SetNumber(KeyStore.MinimizedX, 0);
-        this.props.Document.SetNumber(KeyStore.MinimizedY, 0);
-        SelectionManager.DeselectAll();
+        if (where[0] !== 0 || where[1] !== 0)
+            this.props.Document.SetNumber(KeyStore.MinimizedX, where[0]);
+        if (where[1] !== 0 || where[0] !== 0)
+            this.props.Document.SetNumber(KeyStore.MinimizedY, where[1]);
     }
 
     @undoBatch
@@ -294,7 +295,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
         e.preventDefault();
 
-        !this.isMinimized() && ContextMenu.Instance.addItem({ description: "Minimize", event: this.minimize });
+        !this.isMinimized() && ContextMenu.Instance.addItem({ description: "Minimize", event: () => this.minimize([0, 0]) });
         ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked });
         ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked });
         ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) });
@@ -309,7 +310,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     @action
-    expand = () => this.props.Document.SetBoolean(KeyStore.Minimized, false)
+    expand = (e: React.MouseEvent) => { this.props.Document.SetBoolean(KeyStore.Minimized, false); SelectionManager.SelectDoc(this, e.ctrlKey); }
     isMinimized = () => this.props.Document.GetBoolean(KeyStore.Minimized, false);
     isSelected = () => SelectionManager.IsSelected(this);
     select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
@@ -318,20 +319,26 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />); }
 
+    @computed get minimizedIcon() {
+        let button = this.layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+            this.layout.indexOf("ImageBox") !== -1 ? faImage :
+                this.layout.indexOf("Formatted") !== -1 ? faStickyNote :
+                    this.layout.indexOf("Video") !== -1 ? faFilm :
+                        this.layout.indexOf("Collection") !== -1 ? faObjectGroup :
+                            faCaretUp;
+        return <FontAwesomeIcon icon={button} style={{ width: MINIMIZED_ICON_SIZE, height: MINIMIZED_ICON_SIZE }} className="documentView-minimizedIcon" />
+    }
+
     render() {
         var scaling = this.props.ContentScaling();
         var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%";
         var nativeWidth = this.nativeWidth > 0 ? this.nativeWidth.toString() + "px" : "100%";
 
         if (this.isMinimized()) {
-            let button = this.layout.indexOf("PDFBox") !== -1 ? faFilePdf :
-                this.layout.indexOf("ImageBox") !== -1 ? faImage :
-                    this.layout.indexOf("Formatted") !== -1 ? faStickyNote :
-                        this.layout.indexOf("Collection") !== -1 ? faObjectGroup :
-                            faCaretUp;
-            return <div className="minimized-box" ref={this._mainCont} onClick={this.expand} onDrop={this.onDrop} onPointerDown={this.onPointerDown} >
-                <FontAwesomeIcon icon={button} style={{ width: MINIMIZED_ICON_SIZE, height: MINIMIZED_ICON_SIZE }} className="documentView-minimizedIcon" />
-            </div>
+            return (
+                <div className="minimized-box" ref={this._mainCont} onClick={this.expand} onDrop={this.onDrop} onPointerDown={this.onPointerDown} >
+                    {this.minimizedIcon}
+                </div>);
         }
         return (
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
-- 
cgit v1.2.3-70-g09d2


From 20e34ca5a76eb90b7be5b0ac27daf088013712b6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 18 Apr 2019 16:32:59 -0400
Subject: left buttons

---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../views/collections/collectionFreeForm/MarqueeView.tsx       | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c50597655..17ae20a03 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -112,7 +112,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
         }, false);
-        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
+        if (((e.button === 0 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8b94374fa..3163c2e72 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -70,7 +70,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
+        if (!e.altKey && !e.metaKey && this.props.container.props.active()) {
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
             this._used = false;
@@ -91,12 +91,12 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 this._showOnUp = false;
                 PreviewCursor.Visible = false;
             }
-            if (!this._used && e.buttons === 1 && !e.altKey && !e.metaKey &&
+            if (!this._used && e.buttons === 2 && !e.altKey && !e.metaKey &&
                 (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
                 this._visible = true;
+                e.stopPropagation();
+                e.preventDefault();
             }
-            e.stopPropagation();
-            e.preventDefault();
         }
     }
 
@@ -107,7 +107,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         if (this._showOnUp) {
             PreviewCursor.Show(this.hideCursor, this._downX, this._downY);
             document.addEventListener("keypress", this.onKeyPress, false);
-        } else {
+        } else if (e.button === 2) {
             let mselect = this.marqueeSelect();
             if (!e.shiftKey) {
                 SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
-- 
cgit v1.2.3-70-g09d2


From ac9111d3c06685ef295f7e15481738132eb470f8 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 18 Apr 2019 19:05:56 -0400
Subject: fixed for macbook

---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx   | 8 +++++---
 2 files changed, 6 insertions(+), 4 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 17ae20a03..23c268622 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -112,7 +112,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
         }, false);
-        if (((e.button === 0 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
+        if ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3163c2e72..143e7b917 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -70,7 +70,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (!e.altKey && !e.metaKey && this.props.container.props.active()) {
+        if (!e.metaKey && this.props.container.props.active()) {
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
             this._used = false;
@@ -79,6 +79,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             document.addEventListener("pointermove", this.onPointerMove, true);
             document.addEventListener("pointerup", this.onPointerUp, true);
             document.addEventListener("keydown", this.marqueeCommand, true);
+            if (e.button == 2 || e.altKey)
+                e.stopPropagation();
         }
     }
 
@@ -91,7 +93,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 this._showOnUp = false;
                 PreviewCursor.Visible = false;
             }
-            if (!this._used && e.buttons === 2 && !e.altKey && !e.metaKey &&
+            if (!this._used && (e.buttons === 2 || e.altKey) && !e.metaKey &&
                 (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
                 this._visible = true;
                 e.stopPropagation();
@@ -107,7 +109,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         if (this._showOnUp) {
             PreviewCursor.Show(this.hideCursor, this._downX, this._downY);
             document.addEventListener("keypress", this.onKeyPress, false);
-        } else if (e.button === 2) {
+        } else if (e.button === 2 || e.altKey) {
             let mselect = this.marqueeSelect();
             if (!e.shiftKey) {
                 SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
-- 
cgit v1.2.3-70-g09d2


From af6feb64510490da8d815f41ceb639d693b9eae3 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 19 Apr 2019 16:34:32 -0400
Subject: played with iconifying things in a different way.  fixed some things
 with schemas.

---
 src/Utils.ts                                       |  2 +-
 src/client/documents/Documents.ts                  | 16 ++++
 src/client/northstar/dash-nodes/HistogramBox.scss  |  6 +-
 src/client/northstar/dash-nodes/HistogramBox.tsx   |  2 +-
 src/client/views/DocumentDecorations.tsx           | 42 ++++++----
 src/client/views/Main.tsx                          |  8 +-
 .../views/collections/CollectionSchemaView.scss    |  9 ++-
 .../views/collections/CollectionSchemaView.tsx     |  5 +-
 .../CollectionFreeFormLinkView.tsx                 |  8 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 13 ++--
 .../collections/collectionFreeForm/MarqueeView.tsx |  9 +--
 src/client/views/globalCssVariables.scss           |  2 +
 src/client/views/globalCssVariables.scss.d.ts      |  1 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 26 +++----
 src/client/views/nodes/DocumentContentsView.tsx    |  3 +-
 src/client/views/nodes/DocumentView.scss           | 16 +---
 src/client/views/nodes/DocumentView.tsx            | 71 ++++++++---------
 src/client/views/nodes/FieldView.tsx               |  5 ++
 src/client/views/nodes/IconBox.scss                | 12 +++
 src/client/views/nodes/IconBox.tsx                 | 90 ++++++++++++++++++++++
 src/fields/IconFIeld.ts                            | 25 ++++++
 src/fields/KeyStore.ts                             | 10 +--
 src/server/Message.ts                              |  2 +-
 src/server/ServerUtil.ts                           |  2 +
 24 files changed, 264 insertions(+), 121 deletions(-)
 create mode 100644 src/client/views/nodes/IconBox.scss
 create mode 100644 src/client/views/nodes/IconBox.tsx
 create mode 100644 src/fields/IconFIeld.ts

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index dec6245ef..98f75d3b9 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,7 +1,7 @@
 import v4 = require('uuid/v4');
 import v5 = require("uuid/v5");
 import { Socket } from 'socket.io';
-import { Message, Types, Transferable } from './server/Message';
+import { Message } from './server/Message';
 import { Document } from './fields/Document';
 
 export class Utils {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4febfa7eb..b0bb74d89 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -32,6 +32,9 @@ import { action } from "mobx";
 import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel";
 import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel";
 import { AggregateFunction } from "../northstar/model/idea/idea";
+import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
+import { IconBox } from "../views/nodes/IconBox";
+import { IconField } from "../../fields/IconFIeld";
 
 export interface DocumentOptions {
     x?: number;
@@ -63,6 +66,7 @@ export namespace Documents {
     let videoProto: Document;
     let audioProto: Document;
     let pdfProto: Document;
+    let iconProto: Document;
     const textProtoId = "textProto";
     const histoProtoId = "histoProto";
     const pdfProtoId = "pdfProto";
@@ -72,6 +76,7 @@ export namespace Documents {
     const kvpProtoId = "kvpProto";
     const videoProtoId = "videoProto";
     const audioProtoId = "audioProto";
+    const iconProtoId = "iconProto";
 
     export function initProtos(): Promise<void> {
         return Server.GetFields([textProtoId, histoProtoId, collProtoId, pdfProtoId, imageProtoId, videoProtoId, audioProtoId, webProtoId, kvpProtoId]).then(fields => {
@@ -84,6 +89,7 @@ export namespace Documents {
             videoProto = fields[videoProtoId] as Document || CreateVideoPrototype();
             audioProto = fields[audioProtoId] as Document || CreateAudioPrototype();
             pdfProto = fields[pdfProtoId] as Document || CreatePdfPrototype();
+            iconProto = fields[iconProtoId] as Document || CreateIconPrototype();
         });
     }
     function assignOptions(doc: Document, options: DocumentOptions): Document {
@@ -92,6 +98,8 @@ export namespace Documents {
         if (options.title !== undefined) { doc.SetText(KeyStore.Title, options.title); }
         if (options.page !== undefined) { doc.SetNumber(KeyStore.Page, options.page); }
         if (options.scale !== undefined) { doc.SetNumber(KeyStore.Scale, options.scale); }
+        if (options.width !== undefined) { doc.SetNumber(KeyStore.Width, options.width); }
+        if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
         if (options.viewType !== undefined) { doc.SetNumber(KeyStore.ViewType, options.viewType); }
         if (options.backgroundColor !== undefined) { doc.SetText(KeyStore.BackgroundColor, options.backgroundColor); }
         if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
@@ -139,6 +147,11 @@ export namespace Documents {
         histoProto.SetText(KeyStore.BackgroundLayout, HistogramBox.LayoutString());
         return histoProto;
     }
+    function CreateIconPrototype(): Document {
+        let iconProto = setupPrototypeOptions(iconProtoId, "ICON_PROTO", IconBox.LayoutString(),
+            { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), layoutKeys: [KeyStore.Data] });
+        return iconProto;
+    }
     function CreateTextPrototype(): Document {
         let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(),
             { x: 0, y: 0, width: 300, height: 150, layoutKeys: [KeyStore.Data] });
@@ -203,6 +216,9 @@ export namespace Documents {
     export function TextDocument(options: DocumentOptions = {}) {
         return assignToDelegate(SetInstanceOptions(textProto, options, ["", TextField]).MakeDelegate(), options);
     }
+    export function IconDocument(icon: string, options: DocumentOptions = {}) {
+        return assignToDelegate(SetInstanceOptions(iconProto, { width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), layoutKeys: [KeyStore.Data], layout: IconBox.LayoutString(), ...options }, [icon, IconField]), options);
+    }
     export function PdfDocument(url: string, options: DocumentOptions = {}) {
         return assignToDelegate(SetInstanceOptions(pdfProto, options, [new URL(url), PDFField]).MakeDelegate(), options);
     }
diff --git a/src/client/northstar/dash-nodes/HistogramBox.scss b/src/client/northstar/dash-nodes/HistogramBox.scss
index e899cf15e..06d781263 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.scss
+++ b/src/client/northstar/dash-nodes/HistogramBox.scss
@@ -1,12 +1,12 @@
 .histogrambox-container {
     padding: 0vw;
     position: absolute;
-    top: 0;
-    left:0;
+    top: -50%;
+    left:-50%;
     text-align: center;
     width: 100%;
     height: 100%;
-    background: black;
+    background: black; 
   }
   .histogrambox-xaxislabel {
     position:absolute;
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 0e84ace50..e2ecc8c83 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -146,7 +146,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
         return (
             <Measure onResize={(r: any) => runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height; })}>
                 {({ measureRef }) =>
-                    <div className="histogrambox-container" ref={measureRef} style={{ transform: `translate(-50%, -50%)` }}>
+                    <div className="histogrambox-container" ref={measureRef}>
                         <div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
                             <span className="histogrambox-yaxislabel-text">
                                 {labelY}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index cfb9befd5..da2c7a3be 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,17 +5,18 @@ import { KeyStore } from "../../fields/KeyStore";
 import { ListField } from "../../fields/ListField";
 import { NumberField } from "../../fields/NumberField";
 import { TextField } from "../../fields/TextField";
+import { Document } from "../../fields/Document";
 import { emptyFunction } from "../../Utils";
 import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
 import { SelectionManager } from "../util/SelectionManager";
 import { undoBatch } from "../util/UndoManager";
 import './DocumentDecorations.scss';
 import { MainOverlayTextBox } from "./MainOverlayTextBox";
-import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
 import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
 import React = require("react");
 import { CompileScript } from "../util/Scripting";
+import { IconBox } from "./nodes/IconBox";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -191,6 +192,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             document.addEventListener("pointerup", this.onMinimizeUp);
         }
     }
+
+    @observable _minimizedX = 0;
+    @observable _minimizedY = 0;
     @action
     onMinimizeMove = (e: PointerEvent): void => {
         e.stopPropagation();
@@ -201,12 +205,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let xf = SelectionManager.SelectedDocuments()[0].props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
             let dx = e.pageX - xf[0];
             let dy = e.pageY - xf[1];
-            if (Math.abs(dx) < 20 && Math.abs(dy) < 20)
-                dx = dy = 0;
+            this._minimizedX = e.clientX;
+            this._minimizedY = e.clientY;
+            if (Math.abs(dx) < 20 && Math.abs(dy) < 20) {
+                this._minimizedX = xf[0];
+                this._minimizedY = xf[1];
+            }
             SelectionManager.SelectedDocuments().map(dv => {
-                let where = (dv.props.ScreenToLocalTransform()).scale(dv.props.ContentScaling()).transformDirection(dx, dy);
-                dv.props.Document.SetNumber(KeyStore.MinimizedX, where[0]);
-                dv.props.Document.SetNumber(KeyStore.MinimizedY, where[1]);
+                let minDoc = dv.props.Document.Get(KeyStore.MinimizedDoc);
+                if (minDoc instanceof Document) {
+                    let where = (dv.props.ScreenToLocalTransform()).scale(dv.props.ContentScaling()).transformPoint(this._minimizedX, this._minimizedY);
+                    let minDocument = minDoc as Document;
+                    minDocument.SetNumber(KeyStore.X, where[0] + dv.props.Document.GetNumber(KeyStore.X, 0));
+                    minDocument.SetNumber(KeyStore.Y, where[1] + dv.props.Document.GetNumber(KeyStore.Y, 0));
+                }
             });
         }
     }
@@ -219,6 +231,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             if (Math.abs(dx) < 4 && Math.abs(dy) < 4 && !this._iconifying) {
                 SelectionManager.SelectedDocuments().map(dv => dv.minimize());
                 SelectionManager.DeselectAll();
+            } else {
+                this._minimizedX = this._minimizedY = 0;
             }
             document.removeEventListener("pointermove", this.onMinimizeMove);
             document.removeEventListener("pointerup", this.onMinimizeUp);
@@ -404,24 +418,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (bounds.x === Number.MAX_VALUE || !seldoc) {
             return (null);
         }
-        let minvec = [seldoc.props.Document.GetNumber(KeyStore.MinimizedX, 0), seldoc.props.Document.GetNumber(KeyStore.MinimizedY, 0)];
-        minvec = seldoc.props.ScreenToLocalTransform().scale(seldoc.props.ContentScaling()).inverse().transformDirection(minvec[0], minvec[1]);
-        let selpos = minvec[0] !== 0 || minvec[1] !== 0 ?
-            [minvec[0] - 12 + (!this._iconifying ? 8 : 0), minvec[1] - 12 + (!this._iconifying ? 28 : 0)] :
+        let selpos = this._minimizedX !== 0 || this._minimizedY !== 0 ?
+            [this._minimizedX - 12 + (!this._iconifying ? 8 : 0), this._minimizedY - 12 + (!this._iconifying ? 28 : 0)] :
             [0, this._iconifying ? -18 : 0];
         let minimizeIcon = (
             <div className="documentDecorations-minimizeButton" onPointerDown={this.onMinimizeDown}
                 style={{ transform: `translate(${selpos[0]}px,${selpos[1]}px)`, }}>
-                {SelectionManager.SelectedDocuments().length == 1 ? SelectionManager.SelectedDocuments()[0].minimizedIcon : "..."}
+                {SelectionManager.SelectedDocuments().length == 1 ? IconBox.DocumentIcon(SelectionManager.SelectedDocuments()[0].props.Document.GetText(KeyStore.Layout, "...")) : "..."}
             </div>);
         if (this._iconifying) {
-            let xfpt = seldoc.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            return (<div className="documentDecorations-container" style={{ transform: `translate(${xfpt[0]}px,${xfpt[1]}px` }}>
-                {minimizeIcon}
-            </div>);
+            return (<div className="documentDecorations-container" > {minimizeIcon} </div>);
         }
-        // console.log(this._documents.length)
-        // let test = this._documents[0].props.Document.Title;
+
         if (this.Hidden) {
             return (null);
         }
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 503a11b35..09ef30f6b 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -85,11 +85,11 @@ export class Main extends React.Component {
         this.initEventListeners();
         this.initAuthenticationRouters();
 
-        // try {
-        //     this.initializeNorthstar();
-        // } catch (e) {
+        try {
+            this.initializeNorthstar();
+        } catch (e) {
 
-        // }
+        }
     }
 
     componentDidMount() { window.onpopstate = this.onHistory; }
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 6eabbe17c..cfdb3ab22 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -1,6 +1,7 @@
 @import "../globalCssVariables";
 
 
+
 .collectionSchemaView-container {
     border-width: $COLLECTION_BORDER_WIDTH;
     border-color : $intermediate-color;
@@ -10,6 +11,10 @@
     position: absolute;
     width: 100%;
     height: 100%;
+
+    .collectionSchemaView-cellContents {
+        height: $MAX_ROW_HEIGHT;
+    }
     
     .collectionSchemaView-previewRegion {
         position: relative;
@@ -104,7 +109,7 @@
         }
         .rt-tr-group {
             direction: ltr;
-            max-height: 44px;
+            max-height: $MAX_ROW_HEIGHT;
         }
         .rt-td {
             border-width: 1px;
@@ -136,7 +141,7 @@
     }
     .ReactTable .rt-th,
     .ReactTable .rt-td {
-        max-height: 44;
+        max-height: $MAX_ROW_HEIGHT;
         padding: 3px 7px;
         font-size: 13px;
         text-align: center;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b61eb342d..90077b053 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -5,6 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, untracked } from "mobx";
 import { observer } from "mobx-react";
 import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
+import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'
 import "react-table/react-table.css";
 import { Document } from "../../../fields/Document";
 import { Field, Opt } from "../../../fields/Field";
@@ -99,11 +100,11 @@ export class CollectionSchemaView extends CollectionSubView {
             return false;
         };
         return (
-            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.Document.Id} ref={reference}>
+            <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document.Id} ref={reference}>
                 <EditableView
                     display={"inline"}
                     contents={contents}
-                    height={56}
+                    height={Number(MAX_ROW_HEIGHT)}
                     GetValue={() => {
                         let field = props.Document.Get(props.fieldKey);
                         if (field && field instanceof Field) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 8868f7df0..20c5a84bf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -23,10 +23,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         let l = this.props.LinkDocs;
         let a = this.props.A;
         let b = this.props.B;
-        let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.Width() / 2);
-        let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.Height() / 2);
-        let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.Width() / 2);
-        let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.Height() / 2);
+        let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Width() / 2);
+        let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Height() / 2);
+        let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Width() / 2);
+        let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Height() / 2);
         return (
             <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown}
                 style={{ strokeWidth: `${l.length * 5}` }}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c50597655..945c01059 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -21,6 +21,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { BooleanField } from "../../../../fields/BooleanField";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -71,17 +72,15 @@ export class CollectionFreeFormView extends CollectionSubView {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
-            console.log("DROP Aat " + de.x + " off " + de.data.xOffset);
             const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
                 let dragDoc = de.data.droppedDocuments[0];
                 let dropX = dragDoc.GetNumber(KeyStore.X, 0);
                 let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
                 de.data.droppedDocuments.map(d => {
-                    let minimized = d.GetBoolean(KeyStore.Minimized, false);
-                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - (minimized ? d.GetNumber(KeyStore.MinimizedX, 0) : 0)) - dropX);
-                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - (minimized ? d.GetNumber(KeyStore.MinimizedY, 0) : 0)) - dropY);
-                    if (!minimized) {
+                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0)) - dropX);
+                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0)) - dropY);
+                    if (!d.GetBoolean(KeyStore.IsMinimized, false)) {
                         if (!d.GetNumber(KeyStore.Width, 0)) {
                             d.SetNumber(KeyStore.Width, 300);
                         }
@@ -264,7 +263,9 @@ export class CollectionFreeFormView extends CollectionSubView {
         let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
             if (page === curPage || page === -1) {
-                prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+                let minim = doc.GetT(KeyStore.IsMinimized, BooleanField);
+                if (minim === undefined || (minim && !minim.Data))
+                    prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
             }
             return prev;
         }, [] as JSX.Element[]);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8b94374fa..bf918beba 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -208,12 +208,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let selRect = this.Bounds;
         let selection: Document[] = [];
         this.props.activeDocuments().map(doc => {
-            let minimized = doc.GetBoolean(KeyStore.Minimized, false);
             var z = doc.GetNumber(KeyStore.Zoom, 1);
-            var x = doc.GetNumber(KeyStore.X, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedX, 0) : 0);
-            var y = doc.GetNumber(KeyStore.Y, 0) + (minimized ? doc.GetNumber(KeyStore.MinimizedY, 0) : 0);
-            var w = minimized ? MINIMIZED_ICON_SIZE : doc.Width() / z;
-            var h = minimized ? MINIMIZED_ICON_SIZE : doc.Height() / z;
+            var x = doc.GetNumber(KeyStore.X, 0);
+            var y = doc.GetNumber(KeyStore.Y, 0);
+            var w = doc.Width() / z;
+            var h = doc.Height() / z;
             if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
                 selection.push(doc);
             }
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index f154f8158..4f68b71b0 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -24,8 +24,10 @@ $docDecorations-zindex: 998; // then doc decorations appear over everything else
 $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
 $COLLECTION_BORDER_WIDTH: 1;
 $MINIMIZED_ICON_SIZE:25;
+$MAX_ROW_HEIGHT: 44px;
 :export  {
     contextMenuZindex: $contextMenu-zindex;
     COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH;
     MINIMIZED_ICON_SIZE: $MINIMIZED_ICON_SIZE;
+    MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
 }
\ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index cc77d987a..9788d31f7 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -3,6 +3,7 @@ interface IGlobalScss {
     contextMenuZindex: string;  // context menu shows up over everything
     COLLECTION_BORDER_WIDTH: string;
     MINIMIZED_ICON_SIZE: string;
+    MAX_ROW_HEIGHT: string;
 }
 declare const globalCssVariables: IGlobalScss;
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index a3689414d..1d42b3899 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -46,10 +46,10 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
     }
 
     get X() {
-        return this.props.Document.GetNumber(KeyStore.X, 0) + (this.isMinimized ? this.props.Document.GetNumber(KeyStore.MinimizedX, 0) : 0);
+        return this.props.Document.GetNumber(KeyStore.X, 0);
     }
     get Y() {
-        return this.props.Document.GetNumber(KeyStore.Y, 0) + (this.isMinimized ? this.props.Document.GetNumber(KeyStore.MinimizedY, 0) : 0);
+        return this.props.Document.GetNumber(KeyStore.Y, 0);
     }
     getTransform = (): Transform =>
         this.props.ScreenToLocalTransform()
@@ -57,8 +57,8 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
             .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     contentScaling = () => this.nativeWidth > 0 ? this.width / this.nativeWidth : 1;
-    panelWidth = () => this.isMinimized ? 10 : this.props.PanelWidth();
-    panelHeight = () => this.isMinimized ? 10 : this.props.PanelHeight();
+    panelWidth = () => this.props.PanelWidth();
+    panelHeight = () => this.props.PanelHeight();
 
     @computed
     get docView() {
@@ -70,19 +70,17 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         />;
     }
 
-    get isMinimized() { return this.props.Document.GetBoolean(KeyStore.Minimized, false); }
-
     render() {
         let zoomFade = 1;
         //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
-        let transform = this.getTransform().scale(this.contentScaling()).inverse();
-        var [sptX, sptY] = transform.transformPoint(0, 0);
-        let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-        let w = bptX - sptX;
-        //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
-        let fadeUp = .75 * 1800;
-        let fadeDown = .075 * 1800;
-        zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+        // let transform = this.getTransform().scale(this.contentScaling()).inverse();
+        // var [sptX, sptY] = transform.transformPoint(0, 0);
+        // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
+        // let w = bptX - sptX;
+        // //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+        // let fadeUp = .75 * 1800;
+        // let fadeDown = .075 * 1800;
+        // zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
 
         return (
             <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 76f852601..07599c345 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -15,6 +15,7 @@ import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
+import { IconBox } from "./IconBox";
 import { KeyValueBox } from "./KeyValueBox";
 import { PDFBox } from "./PDFBox";
 import { VideoBox } from "./VideoBox";
@@ -62,7 +63,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
             return <p>Error loading layout keys</p>;
         }
         return <JsxParser
-            components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings()}
             jsx={this.layout}
             showWarnings={true}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index a0e829d26..7c72fb6e6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,7 +4,7 @@
   position: inherit;
   top: 0;
   left:0;
-  background: $light-color; //overflow: hidden;
+ // background: $light-color; //overflow: hidden;
   transform-origin: left top;
 
   &.minimized {
@@ -30,18 +30,4 @@
 }
 .documentView-node-topmost {
     background: white;
-}
-.minimized-box {
-    position: absolute;
-    left:0;
-    top:0;
-    width:$MINIMIZED_ICON_SIZE;
-    height:$MINIMIZED_ICON_SIZE;
-    transform:translate(-50%, -50%);
-}
-
-.minimized-box:hover {
-  background: $main-accent;
-  transform:translate(-50%, -50%) scale(1.15);;
-  cursor: pointer;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3ada3252c..558cbf017 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,3 @@
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faObjectGroup, faStickyNote, faFilePdf, faFilm, faImage } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
@@ -20,18 +17,11 @@ import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
-import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
 import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
-
-
-library.add(faCaretUp);
-library.add(faObjectGroup);
-library.add(faStickyNote);
-library.add(faFilePdf);
-library.add(faFilm);
+import { TextField } from "../../../fields/TextField";
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
@@ -189,10 +179,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
         e.stopPropagation();
-        if (!SelectionManager.IsSelected(this) && e.button !== 2 &&
-            Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
-            SelectionManager.SelectDoc(this, e.ctrlKey);
-        }
+        if (!SelectionManager.IsSelected(this) && e.button !== 2)
+            if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
+                SelectionManager.SelectDoc(this, e.ctrlKey);
+            }
     }
     stopPropagation = (e: React.SyntheticEvent) => {
         e.stopPropagation();
@@ -221,13 +211,33 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
     }
 
+    @action createIcon = (layoutString: string): void => {
+        let iconDoc = Documents.IconDocument(layoutString);
+        iconDoc.SetBoolean(KeyStore.IsMinimized, false);
+        iconDoc.SetNumber(KeyStore.NativeWidth, 0);
+        iconDoc.SetNumber(KeyStore.NativeHeight, 0);
+        iconDoc.Set(KeyStore.Prototype, this.props.Document);
+        iconDoc.Set(KeyStore.MaximizedDoc, this.props.Document);
+        this.props.Document.Set(KeyStore.MinimizedDoc, iconDoc);
+        this.props.addDocument && this.props.addDocument(iconDoc, false);
+    }
+
     @action
-    public minimize = (where: number[]): void => {
-        this.props.Document.SetBoolean(KeyStore.Minimized, true);
-        if (where[0] !== 0 || where[1] !== 0)
-            this.props.Document.SetNumber(KeyStore.MinimizedX, where[0]);
-        if (where[1] !== 0 || where[0] !== 0)
-            this.props.Document.SetNumber(KeyStore.MinimizedY, where[1]);
+    public minimize = (): void => {
+        this.props.Document.SetBoolean(KeyStore.IsMinimized, true);
+        this.props.Document.GetAsync(KeyStore.MinimizedDoc, mindoc => {
+            if (mindoc === undefined) {
+                this.props.Document.GetAsync(KeyStore.BackgroundLayout, field => {
+                    if (field instanceof TextField) this.createIcon(field.Data);
+                    else this.props.Document.GetAsync(KeyStore.Layout, field => {
+                        if (field instanceof TextField) this.createIcon(field.Data);
+                    });
+                });
+            }
+            else if (mindoc instanceof Document) {
+                this.props.addDocument && this.props.addDocument(mindoc, false);
+            }
+        });
     }
 
     @undoBatch
@@ -295,7 +305,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
         e.preventDefault();
 
-        !this.isMinimized() && ContextMenu.Instance.addItem({ description: "Minimize", event: () => this.minimize([0, 0]) });
         ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked });
         ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked });
         ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) });
@@ -309,9 +318,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             SelectionManager.SelectDoc(this, false);
     }
 
-    @action
-    expand = (e: React.MouseEvent) => { this.props.Document.SetBoolean(KeyStore.Minimized, false); SelectionManager.SelectDoc(this, e.ctrlKey); }
-    isMinimized = () => this.props.Document.GetBoolean(KeyStore.Minimized, false);
     isSelected = () => SelectionManager.IsSelected(this);
     select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
 
@@ -319,27 +325,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />); }
 
-    @computed get minimizedIcon() {
-        let button = this.layout.indexOf("PDFBox") !== -1 ? faFilePdf :
-            this.layout.indexOf("ImageBox") !== -1 ? faImage :
-                this.layout.indexOf("Formatted") !== -1 ? faStickyNote :
-                    this.layout.indexOf("Video") !== -1 ? faFilm :
-                        this.layout.indexOf("Collection") !== -1 ? faObjectGroup :
-                            faCaretUp;
-        return <FontAwesomeIcon icon={button} style={{ width: MINIMIZED_ICON_SIZE, height: MINIMIZED_ICON_SIZE }} className="documentView-minimizedIcon" />
-    }
 
     render() {
         var scaling = this.props.ContentScaling();
         var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%";
         var nativeWidth = this.nativeWidth > 0 ? this.nativeWidth.toString() + "px" : "100%";
 
-        if (this.isMinimized()) {
-            return (
-                <div className="minimized-box" ref={this._mainCont} onClick={this.expand} onDrop={this.onDrop} onPointerDown={this.onPointerDown} >
-                    {this.minimizedIcon}
-                </div>);
-        }
         return (
             <div className={`documentView-node${this.props.isTopMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index e9f6950ff..93e385821 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -23,6 +23,8 @@ import { returnFalse, emptyDocFunction, emptyFunction, returnOne } from "../../.
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
+import { IconField } from "../../../fields/IconFIeld";
+import { IconBox } from "./IconBox";
 
 
 //
@@ -72,6 +74,9 @@ export class FieldView extends React.Component<FieldViewProps> {
         else if (field instanceof ImageField) {
             return <ImageBox {...this.props} />;
         }
+        else if (field instanceof IconField) {
+            return <IconBox {...this.props} />;
+        }
         else if (field instanceof VideoField) {
             return <VideoBox {...this.props} />;
         }
diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss
new file mode 100644
index 000000000..ce0ee2e09
--- /dev/null
+++ b/src/client/views/nodes/IconBox.scss
@@ -0,0 +1,12 @@
+
+@import "../globalCssVariables";
+.iconBox-container {
+    position: absolute;
+    left:0;
+    top:0;
+    svg {
+        width: 100% !important;
+        height: 100%;
+        background: white;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
new file mode 100644
index 000000000..5ada2186d
--- /dev/null
+++ b/src/client/views/nodes/IconBox.tsx
@@ -0,0 +1,90 @@
+import React = require("react");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed } from "mobx";
+import { observer } from "mobx-react";
+import { Document } from '../../../fields/Document';
+import { IconField } from "../../../fields/IconFIeld";
+import { KeyStore } from "../../../fields/KeyStore";
+import { SelectionManager } from "../../util/SelectionManager";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./IconBox.scss";
+
+
+library.add(faCaretUp);
+library.add(faObjectGroup);
+library.add(faStickyNote);
+library.add(faFilePdf);
+library.add(faFilm);
+
+@observer
+export class IconBox extends React.Component<FieldViewProps> {
+    public static LayoutString() { return FieldView.LayoutString(IconBox); }
+
+    @computed get maximized() { return this.props.Document.GetT(KeyStore.MaximizedDoc, Document); }
+    @computed get layout(): string { return this.props.Document.GetData(this.props.fieldKey, IconField, "<p>Error loading layout data</p>" as string); }
+    @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
+
+    public static DocumentIcon(layout: string) {
+        let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+            layout.indexOf("ImageBox") !== -1 ? faImage :
+                layout.indexOf("Formatted") !== -1 ? faStickyNote :
+                    layout.indexOf("Video") !== -1 ? faFilm :
+                        layout.indexOf("Collection") !== -1 ? faObjectGroup :
+                            faCaretUp;
+        return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />
+    }
+
+    animateTransition(icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
+        setTimeout(() => {
+            let now = Date.now();
+            let progress = Math.min(1, (now - stime) / 200);
+            let pval = maximizing ?
+                [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
+                [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
+            target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
+            target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
+            target.SetNumber(KeyStore.X, pval[0]);
+            target.SetNumber(KeyStore.Y, pval[1]);
+            if (now < stime + 200) {
+                this.animateTransition(icon, targ, width, height, stime, target, maximizing);
+            }
+            else {
+                if (!maximizing) {
+                    target.SetBoolean(KeyStore.IsMinimized, true);
+                    target.SetNumber(KeyStore.X, targ[0]);
+                    target.SetNumber(KeyStore.Y, targ[1]);
+                    target.SetNumber(KeyStore.Width, width);
+                    target.SetNumber(KeyStore.Height, height);
+                }
+                this._completed = true;
+            }
+        },
+            2);
+    }
+
+    _completed = true;
+
+    @action
+    public toggleMinimize = (): void => {
+        SelectionManager.DeselectAll();
+        if (this.maximized instanceof Document && this._completed) {
+            this._completed = false;
+            let minimized = this.maximized.GetBoolean(KeyStore.IsMinimized, false);
+            this.maximized.SetBoolean(KeyStore.IsMinimized, false);
+            this.animateTransition(
+                [this.props.Document.GetNumber(KeyStore.X, 0), this.props.Document.GetNumber(KeyStore.Y, 0)],
+                [this.maximized.GetNumber(KeyStore.X, 0), this.maximized.GetNumber(KeyStore.Y, 0)],
+                this.maximized.GetNumber(KeyStore.Width, 0), this.maximized.GetNumber(KeyStore.Width, 0),
+                Date.now(), this.maximized, minimized);
+        }
+    }
+
+    render() {
+        return (
+            <div className="iconBox-container" onClick={this.toggleMinimize} >
+                {this.minimizedIcon}
+            </div>);
+    }
+}
\ No newline at end of file
diff --git a/src/fields/IconFIeld.ts b/src/fields/IconFIeld.ts
new file mode 100644
index 000000000..a6694cc49
--- /dev/null
+++ b/src/fields/IconFIeld.ts
@@ -0,0 +1,25 @@
+import { BasicField } from "./BasicField";
+import { FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class IconField extends BasicField<string> {
+    constructor(data: string = "", id?: FieldId, save: boolean = true) {
+        super(data, save, id);
+    }
+
+    ToScriptString(): string {
+        return `new IconField("${this.Data}")`;
+    }
+
+    Copy() {
+        return new IconField(this.Data);
+    }
+
+    ToJson() {
+        return {
+            type: Types.Icon,
+            data: this.Data,
+            id: this.Id
+        };
+    }
+}
\ No newline at end of file
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index ff2f31003..a347f8bcf 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -4,8 +4,6 @@ export namespace KeyStore {
     export const Prototype = new Key("Prototype");
     export const X = new Key("X");
     export const Y = new Key("Y");
-    export const MinimizedX = new Key("MinimizedX");
-    export const MinimizedY = new Key("MinimizedY");
     export const Page = new Key("Page");
     export const Title = new Key("Title");
     export const Author = new Key("Author");
@@ -47,14 +45,16 @@ export namespace KeyStore {
     export const OptionalRightCollection = new Key("OptionalRightCollection");
     export const Archives = new Key("Archives");
     export const Workspaces = new Key("Workspaces");
-    export const Minimized = new Key("Minimized");
+    export const IsMinimized = new Key("IsMinimized");
+    export const MinimizedDoc = new Key("MinimizedDoc");
+    export const MaximizedDoc = new Key("MaximizedDoc");
     export const CopyDraggedItems = new Key("CopyDraggedItems");
 
-    export const KeyList: Key[] = [Prototype, X, Y, MinimizedX, MinimizedY, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
+    export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
         Width, Height, ZIndex, Zoom, 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, Minimized, CopyDraggedItems
+        Archives, Workspaces, IsMinimized, MinimizedDoc, MaximizedDoc, CopyDraggedItems
     ];
     export function KeyLookup(keyid: string) {
         for (const key of KeyList) {
diff --git a/src/server/Message.ts b/src/server/Message.ts
index bbe4ffcad..15916ef12 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -14,7 +14,7 @@ export class Message<T> {
 }
 
 export enum Types {
-    Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference,
+    Number, List, Key, Image, Web, Document, Text, Icon, RichText, DocumentReference,
     Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script,
 }
 
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 818230c1a..79ca5e55d 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -18,6 +18,7 @@ import { NumberField } from "./../fields/NumberField";
 import { RichTextField } from "./../fields/RichTextField";
 import { TextField } from "./../fields/TextField";
 import { Transferable, Types } from "./Message";
+import { IconField } from "../fields/IconFIeld";
 
 export class ServerUtils {
     public static prepend(extension: string): string {
@@ -37,6 +38,7 @@ export class ServerUtils {
             case Types.Boolean: return new BooleanField(json.data, json.id, false);
             case Types.Number: return new NumberField(json.data, json.id, false);
             case Types.Text: return new TextField(json.data, json.id, false);
+            case Types.Icon: return new IconField(json.data, json.id, false);
             case Types.Html: return new HtmlField(json.data, json.id, false);
             case Types.Web: return new WebField(new URL(json.data), json.id, false);
             case Types.RichText: return new RichTextField(json.data, json.id, false);
-- 
cgit v1.2.3-70-g09d2


From 5a4be44ac6b9fdc10a5464623a02891bc2cc4444 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 20 Apr 2019 19:23:27 -0400
Subject: fixed input text scaling

---
 src/client/views/MainOverlayTextBox.tsx            | 27 +++++++++-------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 src/client/views/nodes/FormattedTextBox.tsx        |  6 +++--
 3 files changed, 15 insertions(+), 19 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index a6c7eabf7..af74efc9d 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -21,8 +21,8 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     public static Instance: MainOverlayTextBox;
     @observable public TextDoc?: Document = undefined;
     public TextScroll: number = 0;
-    private _textRect: any;
-    private _textXf: Transform = Transform.Identity();
+    @observable _textRect: any;
+    @observable _textXf: () => Transform = () => Transform.Identity();
     private _textFieldKey: Key = KeyStore.Data;
     private _textColor: string | null = null;
     private _textTargetDiv: HTMLDivElement | undefined;
@@ -35,14 +35,14 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     }
 
     @action
-    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) {
+    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: () => Transform) {
         if (this._textTargetDiv) {
             this._textTargetDiv.style.color = this._textColor;
         }
 
         this.TextDoc = textDoc;
         this._textFieldKey = textFieldKey!;
-        this._textXf = tx ? tx : Transform.Identity();
+        this._textXf = tx ? tx : () => Transform.Identity();
         this._textTargetDiv = div;
         if (div) {
             this._textColor = div.style.color;
@@ -89,22 +89,17 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         document.removeEventListener('pointerup', this.textBoxUp);
     }
 
-    textXf = () => this._textXf;
-
     render() {
         if (this.TextDoc) {
-            let x: number = this._textRect.x;
-            let y: number = this._textRect.y;
-            let w: number = this._textRect.width;
-            let h: number = this._textRect.height;
-            let t = this._textXf.transformPoint(0, 0);
-            let s = this._textXf.transformPoint(1, 0);
-            s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1]));
-            return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${x}px, ${y}px) scale(${1 / s[0]},${1 / s[0]})`, width: "auto", height: "auto" }} >
-                <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} style={{ transform: `scale(${1}, ${1})`, width: `${w * s[0]}px`, height: `${h * s[0]}px` }}>
+            let toScreenXf = this._textXf().inverse();
+            let pt = toScreenXf.transformPoint(0, 0);
+            let s = 1 / this._textXf().Scale;
+            return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${pt[0]}px, ${pt[1]}px) scale(${s},${s})`, width: "auto", height: "auto" }} >
+                <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
+                    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} focus={emptyDocFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 945c01059..9730fc27b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -204,7 +204,6 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     setPan(panX: number, panY: number) {
-        MainOverlayTextBox.Instance.SetTextDoc();
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ae05c2dad..29ed2da13 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -115,7 +115,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
             );
         } else {
             this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
-                () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
+                () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform));
         }
 
 
@@ -186,7 +186,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
 
     onFocused = (e: React.FocusEvent): void => {
         if (!this.props.isOverlay) {
-            MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform());
+            if (MainOverlayTextBox.Instance.TextDoc != this.props.Document) {
+                MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
+            }
         } else {
             if (this._ref.current) {
                 this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
-- 
cgit v1.2.3-70-g09d2


From b63bcb791013766d5d16e4f38964499268f904c4 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 21 Apr 2019 00:03:52 -0400
Subject: fixed a bunch of issues related to moving documents with different
 zoom bases.

---
 src/client/documents/Documents.ts                  |  2 +
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/MainOverlayTextBox.tsx            |  4 +-
 .../views/collections/CollectionBaseView.tsx       | 78 ++++++++++------------
 src/client/views/collections/CollectionSubView.tsx | 38 +++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 10 +--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 19 +++---
 src/fields/KeyStore.ts                             |  4 +-
 10 files changed, 72 insertions(+), 94 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b0bb74d89..0e6661819 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -54,6 +54,7 @@ export interface DocumentOptions {
     viewType?: number;
     backgroundColor?: string;
     copyDraggedItems?: boolean;
+    documentText?: string;
 }
 
 export namespace Documents {
@@ -97,6 +98,7 @@ export namespace Documents {
         if (options.nativeHeight !== undefined) { doc.SetNumber(KeyStore.NativeHeight, options.nativeHeight); }
         if (options.title !== undefined) { doc.SetText(KeyStore.Title, options.title); }
         if (options.page !== undefined) { doc.SetNumber(KeyStore.Page, options.page); }
+        if (options.documentText !== undefined) { doc.SetText(KeyStore.DocumentText, options.documentText); }
         if (options.scale !== undefined) { doc.SetNumber(KeyStore.Scale, options.scale); }
         if (options.width !== undefined) { doc.SetNumber(KeyStore.Width, options.width); }
         if (options.height !== undefined) { doc.SetNumber(KeyStore.Height, options.height); }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 32cf985ce..2af6cb353 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -246,7 +246,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         selViews.map(selDoc => {
             let minDoc = selDoc.props.Document.Get(KeyStore.MinimizedDoc);
             if (minDoc instanceof Document) {
-                let zoom = selDoc.props.Document.GetNumber(KeyStore.Zoom, 1);
+                let zoom = selDoc.props.Document.GetNumber(KeyStore.ZoomBasis, 1);
                 let where = (selDoc.props.ScreenToLocalTransform()).scale(selDoc.props.ContentScaling()).scale(1 / zoom).
                     transformPoint(this._minimizedX - 12, this._minimizedY - 12);
                 minDoc.SetNumber(KeyStore.X, where[0] + selDoc.props.Document.GetNumber(KeyStore.X, 0));
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index af74efc9d..2b72b09c6 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -71,9 +71,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             document.removeEventListener("pointermove", this.textBoxMove);
             document.removeEventListener('pointerup', this.textBoxUp);
             let dragData = new DragManager.DocumentDragData([this.TextDoc!]);
-            const [left, top] = this._textXf
-                .inverse()
-                .transformPoint(0, 0);
+            const [left, top] = this._textXf.inverse().transformPoint(0, 0);
             dragData.xOffset = e.clientX - left;
             dragData.yOffset = e.clientY - top;
             DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 0c1cd7b8f..eec01bb3f 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -8,6 +8,7 @@ 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,
@@ -87,57 +88,50 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
 
     @action.bound
     addDocument(doc: Document, allowDuplicates: boolean = false): boolean {
-        let props = this.props;
-        var curPage = props.Document.GetNumber(KeyStore.CurPage, -1);
+        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
         doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage));
         if (curPage >= 0) {
-            doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document);
+            doc.SetOnPrototype(KeyStore.AnnotationOn, this.props.Document);
         }
-        if (props.Document.Get(props.fieldKey) instanceof Field) {
-            //TODO This won't create the field if it doesn't already exist
-            const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>());
-            if (!this.createsCycle(doc, props.Document)) {
+        if (this.props.Document.Get(this.props.fieldKey) instanceof Field) {
+            const value = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+            if (!this.createsCycle(doc, this.props.Document)) {
                 if (!value.some(v => v.Id === doc.Id) || allowDuplicates) {
                     value.push(doc);
+                    doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
                 }
                 return true;
             }
-            else {
-                return false;
-            }
-        } else {
-            let proto = props.Document.GetPrototype();
-            if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) {
-                const field = new ListField([doc]);
-                // const script = CompileScript(`
-                //     if(added) {
-                //         console.log("added " + field.Title + " " + doc.Title);
-                //     } else {
-                //         console.log("removed " + field.Title + " " + doc.Title);
-                //     }
-                // `, {
-                //         addReturn: false,
-                //         params: {
-                //             field: Document.name,
-                //             added: "boolean"
-                //         },
-                //         capturedVariables: {
-                //             doc: this.props.Document
-                //         }
-                //     });
-                // if (script.compiled) {
-                //     field.addScript(new ScriptField(script));
-                // }
-                props.Document.SetOnPrototype(props.fieldKey, field);
-            }
-            else {
-                return false;
-            }
-        }
-        if (true || this.isAnnotationOverlay) {
-            doc.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
         }
-        return true;
+        // bcz: What is this code trying to do?
+        // else {
+        //     let proto = props.Document.GetPrototype();
+        //     if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) {
+        //         const field = new ListField([doc]);
+        //         // const script = CompileScript(`
+        //         //     if(added) {
+        //         //         console.log("added " + field.Title + " " + doc.Title);
+        //         //     } else {
+        //         //         console.log("removed " + field.Title + " " + doc.Title);
+        //         //     }
+        //         // `, {
+        //         //         addReturn: false,
+        //         //         params: {
+        //         //             field: Document.name,
+        //         //             added: "boolean"
+        //         //         },
+        //         //         capturedVariables: {
+        //         //             doc: this.props.Document
+        //         //         }
+        //         //     });
+        //         // if (script.compiled) {
+        //         //     field.addScript(new ScriptField(script));
+        //         // }
+        //         props.Document.SetOnPrototype(props.fieldKey, field);
+        //         return true;
+        //     }
+        // }
+        return false;
     }
 
     @action.bound
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5c3b2e586..ead559bd9 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -159,10 +159,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
         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);
+            this.props.addDocument(Documents.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text }), false);
             return;
         }
 
@@ -188,36 +185,17 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
             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 formData = new FormData();
+                if (file) formData.append('file', file);
 
-                let prom = fetch(upload, {
+                promises.push(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);
+                }).then(async (res: Response) =>
+                    (await res.json()).map(action((file: any) =>
+                        this.getDocumentFromType(type, window.location.origin + file, { ...options, nativeWidth: 600, width: 300, title: dropFileName }).
+                            then(doc => doc && this.props.addDocument(doc, false))))));
             }
         }
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9730fc27b..83b7f9be4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -49,7 +49,6 @@ export class CollectionFreeFormView extends CollectionSubView {
         this.addDocument(newBox, false);
     }
     private addDocument = (newBox: Document, allowDuplicates: boolean) => {
-        newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
         return this.props.addDocument(this.bringToFront(newBox), false);
     }
     private selectDocuments = (docs: Document[]) => {
@@ -72,9 +71,13 @@ export class CollectionFreeFormView extends CollectionSubView {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
-            const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
                 let dragDoc = de.data.droppedDocuments[0];
+                let zoom = dragDoc.GetNumber(KeyStore.ZoomBasis, 1);
+                let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+                let x = xp - de.data.xOffset / zoom;
+                let y = yp - de.data.yOffset / zoom;
+
                 let dropX = dragDoc.GetNumber(KeyStore.X, 0);
                 let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
                 de.data.droppedDocuments.map(d => {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index bf918beba..318adbe85 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -152,20 +152,20 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             });
             let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField);
             let inkData = ink && ink !== FieldWaiting ? ink.Data : undefined;
-            //setTimeout(() => {
+            let zoomBasis = this.props.container.props.Document.GetNumber(KeyStore.Scale, 1);
             let newCollection = Documents.FreeformDocument(selected, {
                 x: bounds.left,
                 y: bounds.top,
                 panx: 0,
                 pany: 0,
-                width: bounds.width,
-                height: bounds.height,
+                scale: zoomBasis,
+                width: bounds.width * zoomBasis,
+                height: bounds.height * zoomBasis,
                 ink: inkData ? this.marqueeInkSelect(inkData) : undefined,
                 title: "a nested collection"
             });
             this.props.addDocument(newCollection, false);
             this.marqueeInkDelete(inkData);
-            // }, 100);
             this.cleanupInteractions();
             SelectionManager.DeselectAll();
         }
@@ -208,7 +208,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         let selRect = this.Bounds;
         let selection: Document[] = [];
         this.props.activeDocuments().map(doc => {
-            var z = doc.GetNumber(KeyStore.Zoom, 1);
+            var z = doc.GetNumber(KeyStore.ZoomBasis, 1);
             var x = doc.GetNumber(KeyStore.X, 0);
             var y = doc.GetNumber(KeyStore.Y, 0);
             var w = doc.Width() / z;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 01a9f26bf..12e5bdf1f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -20,7 +20,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
     }
 
-    @computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.Zoom, 1); }
+    @computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.ZoomBasis, 1); }
     @computed get zIndex(): number { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); }
     @computed get width(): number { return this.props.Document.Width(); }
     @computed get height(): number { return this.props.Document.Height(); }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c47a56168..0d396fd66 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -110,7 +110,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             }
             e.stopPropagation();
         } else {
-            if (this.active) {
+            let maxdoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
+            if (this.active ||
+                maxdoc instanceof Document // bcz: need a better way of allowing a document to handle pointer events when its not active (ie. be a top-level widget)
+            ) {
                 e.stopPropagation();
                 document.removeEventListener("pointermove", this.onPointerMove);
                 document.addEventListener("pointermove", this.onPointerMove);
@@ -149,11 +152,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
         if (this._mainCont.current) {
-            const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+            const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
             let dragData = new DragManager.DocumentDragData([this.props.Document]);
+            const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
             dragData.aliasOnDrop = dropAliasOfDraggedDoc;
-            dragData.xOffset = x - left;
-            dragData.yOffset = y - top;
+            dragData.xOffset = xoff;
+            dragData.yOffset = yoff;
             dragData.moveDocument = this.props.moveDocument;
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
                 handlers: {
@@ -185,7 +189,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (!SelectionManager.IsSelected(this) && e.button !== 2 &&
             Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
             this.props.Document.GetTAsync(KeyStore.MaximizedDoc, Document).then(maxdoc => {
-                if (maxdoc instanceof Document) {
+                if (maxdoc instanceof Document) {   // bcz: need a better way to associate behaviors with click events on widget-documents
                     this.props.addDocument && this.props.addDocument(maxdoc, false);
                     this.toggleIcon();
                 } else
@@ -202,9 +206,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     fieldsClicked = (e: React.MouseEvent): void => {
-        if (this.props.addDocument) {
-            this.props.addDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
-        }
+        let kvp = Documents.KVPDocument(this.props.Document, { width: 300, height: 300 });
+        CollectionDockingView.Instance.AddRightSplit(kvp);
     }
     fullScreenClicked = (e: React.MouseEvent): void => {
         CollectionDockingView.Instance.OpenFullScreen((this.props.Document.GetPrototype() as Document).MakeDelegate());
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index a347f8bcf..0a96beb50 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -15,7 +15,7 @@ export namespace KeyStore {
     export const Width = new Key("Width");
     export const Height = new Key("Height");
     export const ZIndex = new Key("ZIndex");
-    export const Zoom = new Key("Zoom");
+    export const ZoomBasis = new Key("ZoomBasis");
     export const Data = new Key("Data");
     export const Annotations = new Key("Annotations");
     export const ViewType = new Key("ViewType");
@@ -51,7 +51,7 @@ export namespace KeyStore {
     export const CopyDraggedItems = new Key("CopyDraggedItems");
 
     export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
-        Width, Height, ZIndex, Zoom, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
+        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
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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 5a9e437bbd175b36a161e1d96c8ae873dfe6d105 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 22 Apr 2019 03:39:39 -0400
Subject: More

---
 src/client/views/collections/CollectionSubView.tsx |  6 +++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 43 +++++++++++-----------
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/debug/Test.tsx                                 | 13 +------
 src/new_fields/Doc.ts                              | 13 +++++--
 src/new_fields/List.ts                             |  5 ++-
 src/new_fields/Schema.ts                           | 20 ++++++++--
 src/new_fields/Types.ts                            | 27 ++++++++------
 8 files changed, 74 insertions(+), 55 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3add288fd..1a839fdcb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -20,6 +20,8 @@ import { CollectionPDFView } from "./CollectionPDFView";
 import { CollectionVideoView } from "./CollectionVideoView";
 import { Doc } from "../../../new_fields/Doc";
 import { DocComponent } from "../DocComponent";
+import { listSpec } from "../../../new_fields/Schema";
+import { Cast } from "../../../new_fields/Types";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -50,6 +52,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             this.createDropTarget(ele);
         }
 
+        get children() {
+            return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc));
+        }
+
         @action
         protected setCursorPosition(position: [number, number]) {
             let ind;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 32104b49d..ee7d65d23 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -20,17 +20,19 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { createSchema, makeInterface } from "../../../../new_fields/Schema";
-import { Doc } from "../../../../new_fields/Doc";
-import { FieldValue } from "../../../../new_fields/Types";
+import { Doc, Id } from "../../../../new_fields/Doc";
+import { FieldValue, Cast } from "../../../../new_fields/Types";
+import { pageSchema } from "../../nodes/ImageBox";
+import { List } from "../../../../new_fields/List";
 
 export const panZoomSchema = createSchema({
     panX: "number",
     panY: "number",
-    zoom: "number"
+    scale: "number"
 });
 
-type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema]>;
-const PanZoomDocument: (doc: Doc) => PanZoomDocument = makeInterface(panZoomSchema, positionSchema);
+type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>;
+const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema);
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@@ -41,24 +43,24 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     private get _pheight() { return this.props.PanelHeight(); }
 
     @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
-    @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+    @computed get nativeHeight() { return FieldValue(this.Document.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?
+    private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; }
     private childViews = () => this.views;
-    private panX = () => this.props.Document.GetNumber(KeyStore.PanX, 0);
-    private panY = () => this.props.Document.GetNumber(KeyStore.PanY, 0);
-    private zoomScaling = () => this.props.Document.GetNumber(KeyStore.Scale, 1);
+    private panX = () => FieldValue(this.Document.panX, 0);
+    private panY = () => FieldValue(this.Document.panY, 0);
+    private zoomScaling = () => FieldValue(this.Document.scale, 1);
     private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0;  // shift so pan position is at center of window for non-overlay collections
     private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
     private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
     private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
     private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
-    private addLiveTextBox = (newBox: Document) => {
-        this._selectOnLoaded = newBox.Id;// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+    private addLiveTextBox = (newBox: Doc) => {
+        this._selectOnLoaded = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
         this.addDocument(newBox, false);
     }
-    private addDocument = (newBox: Document, allowDuplicates: boolean) => {
-        newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1));
+    private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
+        newBox.zoom = FieldValue(this.Document.scale, 1);
         return this.props.addDocument(this.bringToFront(newBox), false);
     }
     private selectDocuments = (docs: Document[]) => {
@@ -67,14 +69,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             SelectionManager.SelectDoc(dv!, true));
     }
     public getActiveDocuments = () => {
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
-        return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).reduce((active, doc) => {
-            var page = doc.GetNumber(KeyStore.Page, -1);
-            if (page === curPage || page === -1) {
-                active.push(doc);
-            }
-            return active;
-        }, [] as Document[]);
+        const curPage = FieldValue(this.Document.curPage, -1);
+        return FieldValue(this.children, [] as Doc[]).filter(doc => {
+            var page = Cast(doc.page, "number", -1);
+            return page === curPage || page === -1;
+        });
     }
 
     @undoBatch
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c11aee56e..cd003ba71 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -71,7 +71,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     @undoBatch
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
-            de.data.droppedDocuments.map(action((drop: Document) => {
+            de.data.droppedDocuments.forEach(action((drop: Document) => {
                 let layout = drop.GetText(KeyStore.BackgroundLayout, "");
                 if (layout.indexOf(ImageBox.name) !== -1) {
                     let imgData = this.props.Document.Get(KeyStore.Data);
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 22a39f31e..179f987e0 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,12 +1,10 @@
 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 { createSchema, makeInterface, makeStrictInterface, listSpec } from '../new_fields/Schema';
 import { ImageField } from '../new_fields/URLField';
 import { Doc } from '../new_fields/Doc';
-import { ListSpec } from '../new_fields/Types';
 import { List } from '../new_fields/List';
-const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 const schema1 = createSchema({
     hello: "number",
@@ -22,7 +20,7 @@ type TestDoc = makeInterface<[typeof schema1]>;
 const schema2 = createSchema({
     hello: ImageField,
     test: "boolean",
-    fields: { List: "number" } as ListSpec<number>,
+    fields: listSpec("number"),
     url: "number",
     testDoc: ImageField
 });
@@ -30,13 +28,6 @@ const schema2 = createSchema({
 const Test2Doc = makeStrictInterface(schema2);
 type Test2Doc = makeStrictInterface<typeof schema2>;
 
-const schema3 = createSchema({
-    test: "boolean",
-});
-
-const Test3Doc = makeStrictInterface(schema3);
-type Test3Doc = makeStrictInterface<typeof schema3>;
-
 const assert = (bool: boolean) => {
     if (!bool) throw new Error();
 };
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 5d18cbb2e..60abccce6 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -4,7 +4,7 @@ import { autoObject, SerializationHelper, Deserializable } from "../client/util/
 import { Utils } from "../Utils";
 import { DocServer } from "../client/DocServer";
 import { setter, getter, getField } from "./util";
-import { Cast, FieldCtor } from "./Types";
+import { Cast, ToConstructor } from "./Types";
 
 export type FieldId = string;
 export const HandleUpdate = Symbol("HandleUpdate");
@@ -28,6 +28,7 @@ export const Parent = Symbol("Parent");
 export class ObjectField {
     protected [OnUpdate]?: (diff?: any) => void;
     private [Parent]?: Doc;
+    readonly [Id] = "";
 }
 
 export type Field = number | string | boolean | ObjectField | RefField;
@@ -71,7 +72,7 @@ export namespace Doc {
         const self = doc[Self];
         return new Promise(res => getField(self, key, ignoreProto, res));
     }
-    export function GetTAsync<T extends Field>(doc: Doc, key: string, ctor: FieldCtor<T>, ignoreProto: boolean = false): Promise<T | undefined> {
+    export function GetTAsync<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): Promise<T | undefined> {
         return new Promise(async res => {
             const field = await GetAsync(doc, key, ignoreProto);
             return Cast(field, ctor);
@@ -81,9 +82,15 @@ export namespace Doc {
         const self = doc[Self];
         return getField(self, key, ignoreProto);
     }
-    export function GetT<T extends Field>(doc: Doc, key: string, ctor: FieldCtor<T>, ignoreProto: boolean = false): T | null | undefined {
+    export function GetT<T extends Field>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): T | null | undefined {
         return Cast(Get(doc, key, ignoreProto), ctor) as T | null | undefined;
     }
+    export async function SetOnPrototype(doc: Doc, key: string, value: Field) {
+        const proto = await Cast(doc.prototype, Doc);
+        if (proto) {
+            proto[key] = value;
+        }
+    }
     export function MakeDelegate(doc: Opt<Doc>): Opt<Doc> {
         if (!doc) {
             return undefined;
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index 58b252f7b..f3ec9e2c5 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -6,8 +6,9 @@ import { observable } from "mobx";
 
 @Deserializable("list")
 class ListImpl<T extends Field> extends ObjectField {
-    constructor() {
+    constructor(fields: T[] = []) {
         super();
+        this.__fields = fields;
         const list = new Proxy<this>(this, {
             set: setter,
             get: getter,
@@ -21,7 +22,7 @@ class ListImpl<T extends Field> extends ObjectField {
 
     @serializable(alias("fields", list(autoObject())))
     @observable
-    private __fields: (T | null | undefined)[] = [];
+    private __fields: (T | null | undefined)[];
 
     private [Update] = (diff: any) => {
         console.log(diff);
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 3b7078cb0..59c6db0bd 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -1,5 +1,7 @@
-import { Interface, ToInterface, Cast, FieldCtor, ToConstructor, HasTail, Head, Tail } from "./Types";
+import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType } from "./Types";
 import { Doc, Field, ObjectField } from "./Doc";
+import { URLField } from "./URLField";
+import { List } from "./List";
 
 type AllToInterface<T extends Interface[]> = {
     1: ToInterface<Head<T>> & AllToInterface<Tail<T>>,
@@ -10,10 +12,10 @@ 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 type makeInterface<T extends Interface[]> = Partial<AllToInterface<T>> & Doc;
 // 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) => makeInterface<T, U> {
+export function makeInterface<T extends Interface[]>(...schemas: T): (doc: Doc) => makeInterface<T> {
     let schema: Interface = {};
     for (const s of schemas) {
         for (const key in s) {
@@ -33,7 +35,10 @@ export function makeInterface<T extends Interface[], U extends Doc>(...schemas:
             return true;
         }
     });
-    return function (doc: any) {
+    return function (doc: Doc) {
+        if (!(doc instanceof Doc)) {
+            throw new Error("Currently wrapping a schema in another schema isn't supported");
+        }
         const obj = Object.create(proto, { doc: { value: doc, writable: false } });
         return obj;
     };
@@ -59,6 +64,9 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
         });
     }
     return function (doc: any) {
+        if (!(doc instanceof Doc)) {
+            throw new Error("Currently wrapping a schema in another schema isn't supported");
+        }
         const obj = Object.create(proto);
         obj.__doc = doc;
         return obj;
@@ -69,3 +77,7 @@ export function createSchema<T extends Interface>(schema: T): T & { prototype: T
     schema.prototype = Doc;
     return schema as any;
 }
+
+export function listSpec<U extends ToConstructor<Field>>(type: U): ListSpec<ToType<U>> {
+    return { List: type as any };//TODO Types
+}
\ No newline at end of file
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index f4f66fe5c..fbf002c84 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -1,25 +1,28 @@
 import { Field, Opt, FieldWaiting, FieldResult } from "./Doc";
 import { List } from "./List";
 
-export type ToType<T> =
+export type ToType<T extends ToConstructor<Field> | ListSpec<Field>> =
     T extends "string" ? string :
     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[]): List<Field> } ? never :
     T extends { new(...args: any[]): infer R } ? R : never;
 
-export type ToConstructor<T> =
+export type ToConstructor<T extends Field> =
     T extends string ? "string" :
     T extends number ? "number" :
-    T extends boolean ? "boolean" : new (...args: any[]) => T;
+    T extends boolean ? "boolean" :
+    T extends List<infer U> ? ListSpec<U> :
+    new (...args: any[]) => T;
 
-export type ToInterface<T> = {
+export type ToInterface<T extends Interface> = {
     [P in keyof T]: ToType<T[P]>;
 };
 
-// type ListSpec<T extends Field[]> = { List: FieldCtor<Head<T>> | ListSpec<Tail<T>> };
-export type ListSpec<T> = { List: FieldCtor<T> };
+// type ListSpec<T extends Field[]> = { List: ToContructor<Head<T>> | ListSpec<Tail<T>> };
+export type ListSpec<T extends Field> = { List: ToConstructor<T> };
 
 // type ListType<U extends Field[]> = { 0: List<ListType<Tail<U>>>, 1: ToType<Head<U>> }[HasTail<U> extends true ? 0 : 1];
 
@@ -34,11 +37,9 @@ export interface Interface {
     // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
 }
 
-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): 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>): FieldResult<ToType<T>> | undefined {
+export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: Field | FieldWaiting | undefined, ctor: T): FieldResult<ToType<T>>;
+export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: Field | FieldWaiting | undefined, ctor: T, defaultVal: ToType<T>): ToType<T>;
+export function Cast<T extends ToConstructor<Field> | ListSpec<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;
     }
@@ -58,7 +59,9 @@ export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | u
     return defaultVal;
 }
 
-export function FieldValue<T extends Field, U extends T>(field: Opt<T> | Promise<Opt<T>>, defaultValue: U): T;
+type WithoutList<T extends Field> = T extends List<infer R> ? R[] : T;
+
+export function FieldValue<T extends Field, U extends WithoutList<T>>(field: Opt<T> | Promise<Opt<T>>, defaultValue: U): WithoutList<T>;
 export function FieldValue<T extends Field>(field: Opt<T> | Promise<Opt<T>>): Opt<T>;
 export function FieldValue<T extends Field>(field: Opt<T> | Promise<Opt<T>>, defaultValue?: T): Opt<T> {
     return field instanceof Promise ? defaultValue : field;
-- 
cgit v1.2.3-70-g09d2


From df33fc10169b45706409748cffdeb84cd44cfdf9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 22 Apr 2019 11:44:51 -0400
Subject: fixed webbox's

---
 src/client/views/DocumentDecorations.tsx                    |  5 ++++-
 src/client/views/collections/CollectionSubView.tsx          |  4 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx           |  5 ++---
 src/client/views/nodes/DocumentView.tsx                     |  5 ++---
 src/client/views/nodes/VideoBox.tsx                         |  7 +++----
 src/client/views/nodes/WebBox.tsx                           | 13 ++++++-------
 6 files changed, 19 insertions(+), 20 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2af6cb353..3eefe0b68 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -25,7 +25,7 @@ export const Flyout = higflyout.default;
 @observer
 export class DocumentDecorations extends React.Component<{}, { value: string }> {
     static Instance: DocumentDecorations;
-    private _resizer = "";
+    @observable _resizer = "";
     private _isPointerDown = false;
     private keyinput: React.RefObject<HTMLInputElement>;
     private _documents: DocumentView[] = SelectionManager.SelectedDocuments();
@@ -255,6 +255,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         });
     }
 
+    @action
     onPointerDown = (e: React.PointerEvent): void => {
         e.stopPropagation();
         if (e.button === 0) {
@@ -398,8 +399,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         });
     }
 
+    @action
     onPointerUp = (e: PointerEvent): void => {
         e.stopPropagation();
+        this._resizer = "";
         if (e.button === 0) {
             e.preventDefault();
             this._isPointerDown = false;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index ead559bd9..f9fc7be5a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -141,7 +141,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                 return undefined;
             }
             ctor = Documents.WebDocument;
-            options = { height: options.width, ...options, title: path };
+            options = { height: options.width, ...options, title: path, nativeWidth: undefined };
         }
         return ctor ? ctor(path, options) : undefined;
     }
@@ -174,7 +174,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> {
                 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"];
+                        let type = result["content-type"];
                         if (type) {
                             this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
                                 .then(doc => doc && this.props.addDocument(doc, false));
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 83b7f9be4..e858b9628 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,5 +1,6 @@
-import { action, computed, observable, trace } from "mobx";
+import { action, computed } from "mobx";
 import { observer } from "mobx-react";
+import { BooleanField } from "../../../../fields/BooleanField";
 import { Document } from "../../../../fields/Document";
 import { KeyStore } from "../../../../fields/KeyStore";
 import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
@@ -10,7 +11,6 @@ import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
 import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss";
 import { InkingCanvas } from "../../InkingCanvas";
-import { MainOverlayTextBox } from "../../MainOverlayTextBox";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "../../nodes/DocumentContentsView";
 import { DocumentViewProps } from "../../nodes/DocumentView";
@@ -21,7 +21,6 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { BooleanField } from "../../../../fields/BooleanField";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0d396fd66..c82b86ea9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,6 +5,8 @@ import { Field, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
+import { NumberField } from "../../../fields/NumberField";
+import { TextField } from "../../../fields/TextField";
 import { ServerUtils } from "../../../server/ServerUtil";
 import { emptyFunction, Utils } from "../../../Utils";
 import { Documents } from "../../documents/Documents";
@@ -21,9 +23,6 @@ import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
-import { TextField } from "../../../fields/TextField";
-import { string } from "prop-types";
-import { NumberField } from "../../../fields/NumberField";
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9d7c2bc56..1e6bc58c9 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,13 +1,12 @@
 import React = require("react");
+import { action, computed, IReactionDisposer, trace } from "mobx";
 import { observer } from "mobx-react";
+import Measure from "react-measure";
 import { FieldWaiting, Opt } from '../../../fields/Field';
+import { KeyStore } from "../../../fields/KeyStore";
 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";
 
 @observer
 export class VideoBox extends React.Component<FieldViewProps> {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1edb4d826..1def300dc 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -2,20 +2,17 @@ import "./WebBox.scss";
 import React = require("react");
 import { WebField } from '../../../fields/WebField';
 import { FieldViewProps, FieldView } from './FieldView';
-import { FieldWaiting } from '../../../fields/Field';
+import { FieldWaiting, Opt } from '../../../fields/Field';
 import { observer } from "mobx-react";
-import { computed } from 'mobx';
+import { computed, reaction, IReactionDisposer } from 'mobx';
 import { KeyStore } from '../../../fields/KeyStore';
+import { DocumentDecorations } from "../DocumentDecorations";
 
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
 
     public static LayoutString() { return FieldView.LayoutString(WebBox); }
 
-    constructor(props: FieldViewProps) {
-        super(props);
-    }
-
     @computed get html(): string { return this.props.Document.GetHtml(KeyStore.Data, ""); }
 
     _ignore = 0;
@@ -46,12 +43,14 @@ export class WebBox extends React.Component<FieldViewProps> {
                     <iframe src={path} style={{ position: "absolute", width: "100%", height: "100%" }} />}
             </div>;
 
+        let frozen = !this.props.isSelected() || DocumentDecorations.Instance._resizer;
+
         return (
             <>
                 <div className="webBox-cont"  >
                     {content}
                 </div>
-                {this.props.isSelected() ? (null) : <div onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} style={{ width: "100%", height: "100%", position: "absolute" }} />}
+                {!frozen ? (null) : <div onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} style={{ width: "100%", height: "100%", position: "absolute" }} />}
             </>);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From aa3bf8c34df0030a631a945673cfdfbcd6320a15 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 22 Apr 2019 22:21:51 -0400
Subject: fixing annotations on documents

---
 .../views/collections/CollectionBaseView.tsx       | 11 +++--
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 50 ++++++++++------------
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 3 files changed, 28 insertions(+), 35 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index eec01bb3f..540df82e7 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -93,16 +93,15 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         if (curPage >= 0) {
             doc.SetOnPrototype(KeyStore.AnnotationOn, this.props.Document);
         }
-        if (this.props.Document.Get(this.props.fieldKey) instanceof Field) {
-            const value = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
-            if (!this.createsCycle(doc, this.props.Document)) {
-                if (!value.some(v => v.Id === doc.Id) || allowDuplicates) {
-                    value.push(doc);
+        this.props.Document.GetOrCreateAsync(this.props.fieldKey, ListField, (value: ListField<Document>) => {
+            if (value && !this.createsCycle(doc, this.props.Document)) {
+                if (!value.Data.some(v => v.Id === doc.Id) || allowDuplicates) {
+                    value.Data.push(doc);
                     doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
                 }
                 return true;
             }
-        }
+        });
         // bcz: What is this code trying to do?
         // else {
         //     let proto = props.Document.GetPrototype();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 69a3aac67..6cc16cd79 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,5 +1,6 @@
 import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
+import Measure from "react-measure";
 import { Document } from "../../../../fields/Document";
 import { KeyStore } from "../../../../fields/KeyStore";
 import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
@@ -21,7 +22,6 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { BooleanField } from "../../../../fields/BooleanField";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -48,7 +48,11 @@ export class CollectionFreeFormView extends CollectionSubView {
         this._selectOnLoaded = newBox.Id;// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
         this.addDocument(newBox, false);
     }
+    @action
     private addDocument = (newBox: Document, allowDuplicates: boolean) => {
+        if (this.isAnnotationOverlay) {
+            newBox.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
+        }
         return this.props.addDocument(this.bringToFront(newBox), false);
     }
     private selectDocuments = (docs: Document[]) => {
@@ -71,27 +75,20 @@ export class CollectionFreeFormView extends CollectionSubView {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
+            const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
-                let dragDoc = de.data.droppedDocuments[0];
-                let zoom = dragDoc.GetNumber(KeyStore.ZoomBasis, 1);
-                let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
-                let x = xp - de.data.xOffset / zoom;
-                let y = yp - de.data.yOffset / zoom;
-
-                let dropX = dragDoc.GetNumber(KeyStore.X, 0);
-                let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
+                let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0);
+                let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0);
                 de.data.droppedDocuments.map(d => {
-                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0)) - dropX);
-                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0)) - dropY);
-                    if (!d.GetBoolean(KeyStore.IsMinimized, false)) {
-                        if (!d.GetNumber(KeyStore.Width, 0)) {
-                            d.SetNumber(KeyStore.Width, 300);
-                        }
-                        if (!d.GetNumber(KeyStore.Height, 0)) {
-                            let nw = d.GetNumber(KeyStore.NativeWidth, 0);
-                            let nh = d.GetNumber(KeyStore.NativeHeight, 0);
-                            d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
-                        }
+                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX));
+                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY));
+                    if (!d.GetNumber(KeyStore.Width, 0)) {
+                        d.SetNumber(KeyStore.Width, 300);
+                    }
+                    if (!d.GetNumber(KeyStore.Height, 0)) {
+                        let nw = d.GetNumber(KeyStore.NativeWidth, 0);
+                        let nh = d.GetNumber(KeyStore.NativeHeight, 0);
+                        d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
                     }
                     this.bringToFront(d);
                 });
@@ -114,7 +111,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
         }, false);
-        if ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())) {
+        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
@@ -191,22 +188,21 @@ export class CollectionFreeFormView extends CollectionSubView {
             // if (modes[e.deltaMode] === 'pixels') coefficient = 50;
             // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height??
             let deltaScale = (1 - (e.deltaY / coefficient));
-            if (deltaScale < 0) deltaScale = -deltaScale;
             if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
                 deltaScale = 1 / this.zoomScaling();
             }
             let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
             let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-            let safeScale = Math.abs(localTransform.Scale);
-            this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale));
-            this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+            this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
+            this.setPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
             e.stopPropagation();
         }
     }
 
     @action
     setPan(panX: number, panY: number) {
+        MainOverlayTextBox.Instance.SetTextDoc();
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
@@ -265,9 +261,7 @@ export class CollectionFreeFormView extends CollectionSubView {
         let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
             if (page === curPage || page === -1) {
-                let minim = doc.GetT(KeyStore.IsMinimized, BooleanField);
-                if (minim === undefined || (minim && !minim.Data))
-                    prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+                prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
             }
             return prev;
         }, [] as JSX.Element[]);
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 12e5bdf1f..9fa6a4f51 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -72,7 +72,7 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
 
     render() {
         let zoomFade = 1;
-        //var zoom = doc.GetNumber(KeyStore.Zoom, 1);
+        //var zoom = doc.GetNumber(KeyStore.ZoomBasis, 1);
         // let transform = this.getTransform().scale(this.contentScaling()).inverse();
         // var [sptX, sptY] = transform.transformPoint(0, 0);
         // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-- 
cgit v1.2.3-70-g09d2


From 45488acf628c5e1d598a79a38ef29cdb26889502 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 22 Apr 2019 22:52:38 -0400
Subject: fixed bringtofront for new annotations

---
 .../collectionFreeForm/CollectionFreeFormView.tsx            | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4cf4ea536..2b96f3095 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -33,7 +33,6 @@ export class CollectionFreeFormView extends CollectionSubView {
     @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?
-    private childViews = () => this.views;
     private panX = () => this.props.Document.GetNumber(KeyStore.PanX, 0);
     private panY = () => this.props.Document.GetNumber(KeyStore.PanY, 0);
     private zoomScaling = () => this.props.Document.GetNumber(KeyStore.Scale, 1);
@@ -51,7 +50,9 @@ export class CollectionFreeFormView extends CollectionSubView {
         if (this.isAnnotationOverlay) {
             newBox.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
         }
-        return this.props.addDocument(this.bringToFront(newBox), false);
+        this.props.addDocument(newBox, false);
+        this.bringToFront(newBox);
+        return newBox;
     }
     private selectDocuments = (docs: Document[]) => {
         SelectionManager.DeselectAll;
@@ -219,12 +220,13 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     bringToFront(doc: Document) {
-        this.props.Document.GetList(this.props.fieldKey, [] as Document[]).slice().sort((doc1, doc2) => {
+        let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).slice();
+        docs.sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
             if (doc2 === doc) return -1;
             return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
         }).map((doc, index) => doc.SetNumber(KeyStore.ZIndex, index + 1));
-        return doc;
+        doc.SetNumber(KeyStore.ZIndex, docs.length + 1);
     }
 
     focusDocument = (doc: Document) => {
@@ -274,6 +276,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)} />];
     render() {
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
@@ -284,7 +287,6 @@ export class CollectionFreeFormView extends CollectionSubView {
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
                     <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
                         zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
-                        <CollectionFreeFormBackgroundView {...this.getDocumentViewProps(this.props.Document)} />
                         <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
                             <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} >
                                 {this.childViews}
-- 
cgit v1.2.3-70-g09d2


From 94d624180a0afc906bfdbabf268b3c735f7c779e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 22 Apr 2019 23:06:14 -0400
Subject: fixed text panning

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 2 +-
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx       | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2b96f3095..0e67b43c2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -20,6 +20,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
+import { MainOverlayTextBox } from "../../MainOverlayTextBox";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -201,7 +202,6 @@ export class CollectionFreeFormView extends CollectionSubView {
 
     @action
     setPan(panX: number, panY: number) {
-        MainOverlayTextBox.Instance.SetTextDoc();
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index fb4396c05..3c9603e15 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -69,8 +69,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         document.removeEventListener("keypress", this.onKeyPress, false);
     }
     @action
-    onPointerDown = (e: React.PointerEvent): void => {
-        if (!e.metaKey && (e.altKey || this.props.container.props.active())) {
+    onPointerDown = (e: React.PointerEvent): void => { // bcz:RightBtnDrag
+        if (e.buttons === 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
             this._used = false;
-- 
cgit v1.2.3-70-g09d2


From 64ffb725360b3a5f96aa45948e21e8836ede1479 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 22 Apr 2019 23:29:43 -0400
Subject: bad merge reversed?

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 25 +++++++++++++++-------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 2 files changed, 18 insertions(+), 9 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0e67b43c2..4144219ad 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -21,6 +21,7 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { MainOverlayTextBox } from "../../MainOverlayTextBox";
+import { BooleanField } from "../../../../fields/BooleanField";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
@@ -48,12 +49,9 @@ export class CollectionFreeFormView extends CollectionSubView {
     }
     @action
     private addDocument = (newBox: Document, allowDuplicates: boolean) => {
-        if (this.isAnnotationOverlay) {
-            newBox.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
-        }
         this.props.addDocument(newBox, false);
         this.bringToFront(newBox);
-        return newBox;
+        return true;
     }
     private selectDocuments = (docs: Document[]) => {
         SelectionManager.DeselectAll;
@@ -75,8 +73,12 @@ export class CollectionFreeFormView extends CollectionSubView {
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) {
-            const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
+                let dragDoc = de.data.droppedDocuments[0];
+                let zoom = dragDoc.GetNumber(KeyStore.ZoomBasis, 1);
+                let [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+                let x = xp - de.data.xOffset / zoom;
+                let y = yp - de.data.yOffset / zoom;
                 let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0);
                 let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0);
                 de.data.droppedDocuments.map(d => {
@@ -111,6 +113,8 @@ export class CollectionFreeFormView extends CollectionSubView {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
         }, false);
+        // if ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())) {
+
         if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
@@ -191,11 +195,13 @@ export class CollectionFreeFormView extends CollectionSubView {
             if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
                 deltaScale = 1 / this.zoomScaling();
             }
+            if (deltaScale < 0) deltaScale = -deltaScale;
             let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
             let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-            this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale);
-            this.setPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale);
+            let safeScale = Math.abs(localTransform.Scale);
+            this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale));
+            this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
             e.stopPropagation();
         }
     }
@@ -261,7 +267,9 @@ export class CollectionFreeFormView extends CollectionSubView {
         let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
             var page = doc.GetNumber(KeyStore.Page, -1);
             if (page === curPage || page === -1) {
-                prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+                let minim = doc.GetT(KeyStore.IsMinimized, BooleanField);
+                if (minim === undefined || (minim && !minim.Data))
+                    prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
             }
             return prev;
         }, [] as JSX.Element[]);
@@ -287,6 +295,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
                     <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} >
                                 {this.childViews}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3c9603e15..c551a67fe 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -69,7 +69,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         document.removeEventListener("keypress", this.onKeyPress, false);
     }
     @action
-    onPointerDown = (e: React.PointerEvent): void => { // bcz:RightBtnDrag
+    onPointerDown = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
-- 
cgit v1.2.3-70-g09d2


From 448a62581fbbad3af8073a195ca45aba2270d239 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 23 Apr 2019 01:14:45 -0400
Subject: right btn option for mac and windows.

---
 src/client/views/MainOverlayTextBox.tsx                      |  2 +-
 src/client/views/collections/CollectionBaseView.tsx          | 12 +++++++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx            | 11 ++++++-----
 .../views/collections/collectionFreeForm/MarqueeView.tsx     | 12 +++++++-----
 src/client/views/nodes/DocumentView.tsx                      |  3 ++-
 5 files changed, 23 insertions(+), 17 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 2b72b09c6..9aaaad084 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -71,7 +71,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             document.removeEventListener("pointermove", this.textBoxMove);
             document.removeEventListener('pointerup', this.textBoxUp);
             let dragData = new DragManager.DocumentDragData([this.TextDoc!]);
-            const [left, top] = this._textXf.inverse().transformPoint(0, 0);
+            const [left, top] = this._textXf().inverse().transformPoint(0, 0);
             dragData.xOffset = e.clientX - left;
             dragData.yOffset = e.clientY - top;
             DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index b19f01350..9ae2993be 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -93,15 +93,17 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         if (curPage >= 0) {
             doc.SetOnPrototype(KeyStore.AnnotationOn, this.props.Document);
         }
-        this.props.Document.GetOrCreateAsync(this.props.fieldKey, ListField, (value: ListField<Document>) => {
-            if (value && !this.createsCycle(doc, this.props.Document)) {
+        if (!this.createsCycle(doc, this.props.Document)) {
+            doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
+            let value = this.props.Document.Get(this.props.fieldKey) as ListField<Document>;
+            if (value) {
                 if (!value.Data.some(v => v.Id === doc.Id) || allowDuplicates) {
                     value.Data.push(doc);
-                    doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
                 }
-                return true;
+            } else {
+                this.props.Document.Set(this.props.fieldKey, new ListField([doc]));
             }
-        });
+        }
         return true;
         // bcz: What is this code trying to do?
         // else {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4144219ad..117343ef3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -20,11 +20,11 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { MainOverlayTextBox } from "../../MainOverlayTextBox";
 import { BooleanField } from "../../../../fields/BooleanField";
 
 @observer
 export class CollectionFreeFormView extends CollectionSubView {
+    public static RIGHT_BTN_DRAG = false;
     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;
@@ -113,9 +113,11 @@ export class CollectionFreeFormView extends CollectionSubView {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
         }, false);
-        // if ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())) {
-
-        if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) {
+        if ((CollectionFreeFormView.RIGHT_BTN_DRAG &&
+            (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) ||
+                (e.button === 0 && e.altKey)) && (childSelected || this.props.active()))) ||
+            (!CollectionFreeFormView.RIGHT_BTN_DRAG &&
+                ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())))) {
             document.removeEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
@@ -295,7 +297,6 @@ export class CollectionFreeFormView extends CollectionSubView {
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
                     <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} >
                                 {this.childViews}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3adcb11bd..8455323fd 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -70,7 +70,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     }
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        if (e.buttons === 1 && !e.altKey && !e.metaKey && this.props.container.props.active()) {
+        if ((e.button === 0 && !e.altKey && !e.metaKey && this.props.container.props.active()) ||
+            (!CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && this.props.container.props.active())) {
             this._downX = this._lastX = e.pageX;
             this._downY = this._lastY = e.pageY;
             this._used = false;
@@ -79,8 +80,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             document.addEventListener("pointermove", this.onPointerMove, true);
             document.addEventListener("pointerup", this.onPointerUp, true);
             document.addEventListener("keydown", this.marqueeCommand, true);
-            if (e.button == 2 || e.altKey)
-                e.stopPropagation();
         }
         if (e.altKey)
             e.preventDefault();
@@ -95,7 +94,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
                 this._showOnUp = false;
                 PreviewCursor.Visible = false;
             }
-            if (!this._used && e.buttons === 1 && !e.metaKey &&
+            if (!this._used &&
+                (!CollectionFreeFormView.RIGHT_BTN_DRAG && (e.buttons === 2 || (e.buttons == 1 && e.altKey))) ||
+                (CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 1 && !e.altKey && !e.metaKey) &&
                 (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
                 this._visible = true;
                 e.stopPropagation();
@@ -113,7 +114,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         if (this._showOnUp) {
             PreviewCursor.Show(this.hideCursor, this._downX, this._downY);
             document.addEventListener("keypress", this.onKeyPress, false);
-        } else if (e.button === 0) {
+        } else if ((CollectionFreeFormView.RIGHT_BTN_DRAG && e.button === 0 && !e.altKey && !e.metaKey) ||
+            (!CollectionFreeFormView.RIGHT_BTN_DRAG && ((e.button === 0 && e.altKey) || e.button === 2))) {
             let mselect = this.marqueeSelect();
             if (!e.shiftKey) {
                 SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c82b86ea9..2f8ac9f20 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -23,6 +23,7 @@ import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
 import "./DocumentView.scss";
 import React = require("react");
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
@@ -98,7 +99,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
         this._downY = e.clientY;
-        if (e.button === 2 && !this.isSelected()) {
+        if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
             return;
         }
         if (e.shiftKey && e.buttons === 2) {
-- 
cgit v1.2.3-70-g09d2


From 49131cc84b0f3e739edb5ffcd64f3acc9469f658 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 23 Apr 2019 12:17:12 -0400
Subject: fixes to zoom scaling, moved and fixed icon stuff into
 CollectionFreeFormDocumentView.  several smaller bugs.

---
 src/client/views/DocumentDecorations.tsx           |  41 ++++-
 src/client/views/Main.tsx                          |   1 +
 .../views/collections/CollectionBaseView.tsx       |   4 +-
 .../views/collections/CollectionDockingView.tsx    |   1 +
 .../views/collections/CollectionSchemaView.tsx     |   3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   1 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 174 +++++++++++++++-----
 src/client/views/nodes/DocumentView.tsx            | 180 +++++----------------
 src/client/views/nodes/ImageBox.tsx                |   5 +-
 9 files changed, 215 insertions(+), 195 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d704b6c47..95910c7be 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -18,6 +18,7 @@ import React = require("react");
 import { CompileScript } from "../util/Scripting";
 import { IconBox } from "./nodes/IconBox";
 import { FieldValue, Field } from "../../fields/Field";
+import { Documents } from "../documents/Documents";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -134,11 +135,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @action
     onBackgroundMove = (e: PointerEvent): void => {
         let dragDocView = SelectionManager.SelectedDocuments()[0];
-        const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+        const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
         let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
+        const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
+        dragData.xOffset = xoff;
+        dragData.yOffset = yoff;
         dragData.aliasOnDrop = false;
-        dragData.xOffset = e.x - left;
-        dragData.yOffset = e.y - top;
         let move = SelectionManager.SelectedDocuments()[0].props.moveDocument;
         dragData.moveDocument = move;
         this.Interacting = this._dragging = true;
@@ -213,12 +215,39 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             this._minimizedX = snapped ? selDocPos[0] + 4 : e.clientX;
             this._minimizedY = snapped ? selDocPos[1] - 18 : e.clientY;
             let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
-            Promise.all(selectedDocs.map(async selDoc => await selDoc.getIconDoc())).then(minDocSet =>
+            Promise.all(selectedDocs.map(async selDoc => await this.getIconDoc(selDoc))).then(minDocSet =>
                 this.moveIconDocs(SelectionManager.SelectedDocuments())
             );
             this._iconifying = snapped;
         }
     }
+
+
+    @action createIcon = (docView: DocumentView, layoutString: string): Document => {
+        let doc = docView.props.Document;
+        let iconDoc = Documents.IconDocument(layoutString);
+        iconDoc.SetText(KeyStore.Title, "ICON" + doc.Title)
+        iconDoc.SetBoolean(KeyStore.IsMinimized, false);
+        iconDoc.SetNumber(KeyStore.NativeWidth, 0);
+        iconDoc.SetNumber(KeyStore.NativeHeight, 0);
+        iconDoc.SetNumber(KeyStore.X, doc.GetNumber(KeyStore.X, 0));
+        iconDoc.SetNumber(KeyStore.Y, doc.GetNumber(KeyStore.Y, 0) - 24);
+        iconDoc.Set(KeyStore.Prototype, doc);
+        iconDoc.Set(KeyStore.MaximizedDoc, doc);
+        doc.Set(KeyStore.MinimizedDoc, iconDoc);
+        docView.props.addDocument && docView.props.addDocument(iconDoc, false);
+        return iconDoc;
+    }
+    @action
+    public getIconDoc = async (docView: DocumentView): Promise<Document | undefined> => {
+        let doc = docView.props.Document;
+        return await doc.GetTAsync(KeyStore.MinimizedDoc, Document).then(async mindoc =>
+            mindoc ? mindoc :
+                await doc.GetTAsync(KeyStore.BackgroundLayout, TextField).then(async field =>
+                    (field instanceof TextField) ? this.createIcon(docView, field.Data) :
+                        await doc.GetTAsync(KeyStore.Layout, TextField).then(field =>
+                            (field instanceof TextField) ? this.createIcon(docView, field.Data) : undefined)));
+    }
     @action
     onMinimizeUp = (e: PointerEvent): void => {
         e.stopPropagation();
@@ -226,7 +255,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             document.removeEventListener("pointermove", this.onMinimizeMove);
             document.removeEventListener("pointerup", this.onMinimizeUp);
             let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd);
-            Promise.all(selectedDocs.map(async selDoc => await selDoc.getIconDoc())).then(minDocSet => {
+            Promise.all(selectedDocs.map(async selDoc => await this.getIconDoc(selDoc))).then(minDocSet => {
                 let minDocs = minDocSet.filter(minDoc => minDoc instanceof Document).map(minDoc => minDoc as Document);
                 minDocs.map(minDoc => {
                     minDoc.SetNumber(KeyStore.X, minDocs[0].GetNumber(KeyStore.X, 0));
@@ -238,7 +267,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     }
                 });
                 runInAction(() => this._minimizedX = this._minimizedY = 0);
-                if (!this._iconifying) selectedDocs[0].toggleIcon();
+                if (!this._iconifying) selectedDocs[0].props.toggleMinimized();
                 this._iconifying = false;
             });
         }
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 09ef30f6b..934616aca 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -187,6 +187,7 @@ export class Main extends React.Component {
                 <div ref={measureRef} id="mainContent-div">
                     {!mainCont ? (null) :
                         <DocumentView Document={mainCont}
+                            toggleMinimized={emptyFunction}
                             addDocument={undefined}
                             removeDocument={undefined}
                             ScreenToLocalTransform={Transform.Identity}
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index 9ae2993be..962cb9b65 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -94,7 +94,6 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             doc.SetOnPrototype(KeyStore.AnnotationOn, this.props.Document);
         }
         if (!this.createsCycle(doc, this.props.Document)) {
-            doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1));
             let value = this.props.Document.Get(this.props.fieldKey) as ListField<Document>;
             if (value) {
                 if (!value.Data.some(v => v.Id === doc.Id) || allowDuplicates) {
@@ -103,6 +102,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             } else {
                 this.props.Document.Set(this.props.fieldKey, new ListField([doc]));
             }
+            // set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
+            doc.GetTAsync(KeyStore.ZoomBasis, NumberField, field => !field &&
+                doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1)));
         }
         return true;
         // bcz: What is this code trying to do?
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e4c647635..1ea18ed42 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -350,6 +350,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             <div className="collectionDockingView-content" ref={this._mainCont}
                 style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                 <DocumentView key={this._document!.Id} Document={this._document!}
+                    toggleMinimized={emptyFunction}
                     addDocument={undefined}
                     removeDocument={undefined}
                     ContentScaling={this.contentScaling}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 90077b053..cb7a67991 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -62,7 +62,7 @@ export class CollectionSchemaView extends CollectionSubView {
 
     @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); }
     @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]); }
-    @computed get borderWidth() { return COLLECTION_BORDER_WIDTH; }
+    @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
 
     renderCell = (rowProps: CellInfo) => {
         let props: FieldViewProps = {
@@ -266,6 +266,7 @@ export class CollectionSchemaView extends CollectionSubView {
             <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}>
                 <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}>
                     <DocumentView Document={this.previewDocument} isTopMost={false} selectOnLoad={false}
+                        toggleMinimized={emptyFunction}
                         addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
                         ScreenToLocalTransform={this.getPreviewTransform}
                         ContentScaling={this.previewContentScaling}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 117343ef3..56d54696d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -247,6 +247,7 @@ export class CollectionFreeFormView extends CollectionSubView {
     getDocumentViewProps(document: Document): DocumentViewProps {
         return {
             Document: document,
+            toggleMinimized: emptyFunction,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 9fa6a4f51..3a4425358 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,12 +1,17 @@
-import { computed, trace } from "mobx";
+import { computed, trace, action } from "mobx";
 import { observer } from "mobx-react";
 import { KeyStore } from "../../../fields/KeyStore";
 import { NumberField } from "../../../fields/NumberField";
+import { Document } from "../../../fields/Document";
 import { Transform } from "../../util/Transform";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import React = require("react");
 import { OmitKeys } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+import { ListField } from "../../../fields/ListField";
+import { BooleanField } from "../../../fields/BooleanField";
+import { matchedData } from "express-validator/filter";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 }
@@ -14,55 +19,48 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
 @observer
 export class CollectionFreeFormDocumentView extends React.Component<CollectionFreeFormDocumentViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
+    private _downX: number = 0;
+    private _downY: number = 0;
 
-    @computed
-    get transform(): string {
+    @computed get transform() {
         return `scale(${this.props.ContentScaling()}, ${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}, ${this.zoom}) `;
     }
-
-    @computed get zoom(): number { return 1 / this.props.Document.GetNumber(KeyStore.ZoomBasis, 1); }
-    @computed get zIndex(): number { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); }
-    @computed get width(): number { return this.props.Document.Width(); }
-    @computed get height(): number { return this.props.Document.Height(); }
-    @computed get nativeWidth(): number { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
-    @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
-
+    @computed get X() { return this.props.Document.GetNumber(KeyStore.X, 0); }
+    @computed get Y() { return this.props.Document.GetNumber(KeyStore.Y, 0); }
+    @computed get zoom() { return 1 / this.props.Document.GetNumber(KeyStore.ZoomBasis, 1); }
+    @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
+    @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
+    @computed get width() { return this.props.Document.Width(); }
+    @computed get height() { return this.props.Document.Height(); }
+    @computed get zIndex() { return this.props.Document.GetNumber(KeyStore.ZIndex, 0); }
     set width(w: number) {
         this.props.Document.SetData(KeyStore.Width, w, NumberField);
         if (this.nativeWidth && this.nativeHeight) {
             this.props.Document.SetNumber(KeyStore.Height, this.nativeHeight / this.nativeWidth * w);
         }
     }
-
     set height(h: number) {
         this.props.Document.SetData(KeyStore.Height, h, NumberField);
         if (this.nativeWidth && this.nativeHeight) {
             this.props.Document.SetNumber(KeyStore.Width, this.nativeWidth / this.nativeHeight * h);
         }
     }
-
     set zIndex(h: number) {
         this.props.Document.SetData(KeyStore.ZIndex, h, NumberField);
     }
 
-    get X() {
-        return this.props.Document.GetNumber(KeyStore.X, 0);
-    }
-    get Y() {
-        return this.props.Document.GetNumber(KeyStore.Y, 0);
-    }
-    getTransform = (): Transform =>
-        this.props.ScreenToLocalTransform()
-            .translate(-this.X, -this.Y)
-            .scale(1 / this.contentScaling()).scale(1 / this.zoom)
-
     contentScaling = () => (this.nativeWidth > 0 ? this.width / this.nativeWidth : 1);
     panelWidth = () => this.props.PanelWidth();
     panelHeight = () => this.props.PanelHeight();
+    toggleMinimized = () => this.toggleIcon();
+    getTransform = (): Transform => this.props.ScreenToLocalTransform()
+        .translate(-this.X, -this.Y)
+        .scale(1 / this.contentScaling()).scale(1 / this.zoom)
 
     @computed
     get docView() {
         return <DocumentView {...OmitKeys(this.props, ['zoomFade'])}
+            toggleMinimized={this.toggleMinimized}
             ContentScaling={this.contentScaling}
             ScreenToLocalTransform={this.getTransform}
             PanelWidth={this.panelWidth}
@@ -70,30 +68,120 @@ export class CollectionFreeFormDocumentView extends React.Component<CollectionFr
         />;
     }
 
+    animateBetweenIcon(first: boolean, icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
+        setTimeout(() => {
+            let now = Date.now();
+            let progress = Math.min(1, (now - stime) / 200);
+            let pval = maximizing ?
+                [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
+                [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
+            target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
+            target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
+            target.SetNumber(KeyStore.X, pval[0]);
+            target.SetNumber(KeyStore.Y, pval[1]);
+            if (first) {
+                target.SetBoolean(KeyStore.IsMinimized, false);
+            }
+            if (now < stime + 200) {
+                this.animateBetweenIcon(false, icon, targ, width, height, stime, target, maximizing);
+            }
+            else {
+                if (!maximizing) {
+                    target.SetBoolean(KeyStore.IsMinimized, true);
+                    target.SetNumber(KeyStore.X, targ[0]);
+                    target.SetNumber(KeyStore.Y, targ[1]);
+                    target.SetNumber(KeyStore.Width, width);
+                    target.SetNumber(KeyStore.Height, height);
+                }
+                (target as any).isIconAnimating = false;
+            }
+        },
+            2);
+    }
+    @action
+    public toggleIcon = async (): Promise<void> => {
+        SelectionManager.DeselectAll();
+        let isMinimized: boolean | undefined;
+        let minDoc = await this.props.Document.GetTAsync(KeyStore.MinimizedDoc, Document);
+        if (!minDoc) return;
+        let minimizedDocSet = await minDoc.GetTAsync(KeyStore.LinkTags, ListField);
+        if (!minimizedDocSet) return;
+        minimizedDocSet.Data.map(async minimizedDoc => {
+            if (minimizedDoc instanceof Document) {
+                this.props.addDocument && this.props.addDocument(minimizedDoc, false);
+                let maximizedDoc = await minimizedDoc.GetTAsync(KeyStore.MaximizedDoc, Document);
+                if (maximizedDoc instanceof Document && !(maximizedDoc as any).isIconAnimating) {
+                    (maximizedDoc as any).isIconAnimating = true;
+                    if (isMinimized === undefined) {
+                        let maximizedDocMinimizedState = await maximizedDoc.GetTAsync(KeyStore.IsMinimized, BooleanField);
+                        isMinimized = (maximizedDocMinimizedState && maximizedDocMinimizedState.Data) ? true : false;
+                    }
+                    let minx = await minimizedDoc.GetTAsync(KeyStore.X, NumberField);
+                    let miny = await minimizedDoc.GetTAsync(KeyStore.Y, NumberField);
+                    let maxx = await maximizedDoc.GetTAsync(KeyStore.X, NumberField);
+                    let maxy = await maximizedDoc.GetTAsync(KeyStore.Y, NumberField);
+                    let maxw = await maximizedDoc.GetTAsync(KeyStore.Width, NumberField);
+                    let maxh = await maximizedDoc.GetTAsync(KeyStore.Height, NumberField);
+                    if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
+                        maxw !== undefined && maxh !== undefined)
+                        this.animateBetweenIcon(
+                            true,
+                            [minx.Data, miny.Data], [maxx.Data, maxy.Data], maxw.Data, maxh.Data,
+                            Date.now(), maximizedDoc, isMinimized);
+                }
+
+            }
+        })
+    }
+    onPointerDown = (e: React.PointerEvent): void => {
+        e.stopPropagation();
+        this._downX = e.clientX;
+        this._downY = e.clientY;
+        document.removeEventListener("pointerup", this.onPointerUp);
+        document.addEventListener("pointerup", this.onPointerUp);
+    }
+    onPointerUp = (e: PointerEvent): void => {
+        document.removeEventListener("pointerup", this.onPointerUp);
+        e.stopPropagation();
+        if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
+            this.props.Document.GetTAsync(KeyStore.MaximizedDoc, Document).then(maxdoc => {
+                if (maxdoc instanceof Document) {   // bcz: need a better way to associate behaviors with click events on widget-documents
+                    SelectionManager.DeselectAll();
+                    this.props.addDocument && this.props.addDocument(maxdoc, false);
+                    this.toggleIcon();
+                }
+            });
+        }
+    }
+
     render() {
+        let maximizedDoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
         let zoomFade = 1;
         //var zoom = doc.GetNumber(KeyStore.ZoomBasis, 1);
-        // let transform = this.getTransform().scale(this.contentScaling()).inverse();
-        // var [sptX, sptY] = transform.transformPoint(0, 0);
-        // let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
-        // let w = bptX - sptX;
-        // //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
-        // let fadeUp = .75 * 1800;
-        // let fadeDown = .075 * 1800;
-        // zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+        let transform = this.getTransform().scale(this.contentScaling()).inverse();
+        var [sptX, sptY] = transform.transformPoint(0, 0);
+        let [bptX, bptY] = transform.transformPoint(this.props.PanelWidth(), this.props.PanelHeight());
+        let w = bptX - sptX;
+        //zoomFade = area < 100 || area > 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1;
+        const screenWidth = 1800;
+        let fadeUp = .75 * screenWidth;
+        let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth;
+        zoomFade = w < fadeDown  /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
 
         return (
-            <div className="collectionFreeFormDocumentView-container" ref={this._mainCont} style={{
-                opacity: zoomFade,
-                transformOrigin: "left top",
-                transform: this.transform,
-                pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
-                width: this.width,
-                height: this.height,
-                position: "absolute",
-                zIndex: this.zIndex,
-                backgroundColor: "transparent"
-            }} >
+            <div className="collectionFreeFormDocumentView-container" ref={this._mainCont}
+                onPointerDown={this.onPointerDown}
+                style={{
+                    opacity: zoomFade,
+                    transformOrigin: "left top",
+                    transform: this.transform,
+                    pointerEvents: (zoomFade < 0.09 ? "none" : "all"),
+                    width: this.width,
+                    height: this.height,
+                    position: "absolute",
+                    zIndex: this.zIndex,
+                    backgroundColor: "transparent"
+                }} >
                 {this.docView}
             </div>
         );
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2f8ac9f20..8922e4b81 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,8 +5,6 @@ import { Field, Opt } from "../../../fields/Field";
 import { Key } from "../../../fields/Key";
 import { KeyStore } from "../../../fields/KeyStore";
 import { ListField } from "../../../fields/ListField";
-import { NumberField } from "../../../fields/NumberField";
-import { TextField } from "../../../fields/TextField";
 import { ServerUtils } from "../../../server/ServerUtil";
 import { emptyFunction, Utils } from "../../../Utils";
 import { Documents } from "../../documents/Documents";
@@ -40,6 +38,7 @@ export interface DocumentViewProps {
     selectOnLoad: boolean;
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
+    toggleMinimized: () => void;
 }
 export interface JsxArgs extends DocumentViewProps {
     Keys: { [name: string]: Key };
@@ -83,7 +82,6 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
 
 @observer
 export class DocumentView extends React.Component<DocumentViewProps> {
-    static _incompleteAnimations: Map<string, boolean> = new Map<string, boolean>();
     private _downX: number = 0;
     private _downY: number = 0;
     private _mainCont = React.createRef<HTMLDivElement>();
@@ -96,42 +94,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
     @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
 
-    onPointerDown = (e: React.PointerEvent): void => {
-        this._downX = e.clientX;
-        this._downY = e.clientY;
-        if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
-            return;
-        }
-        if (e.shiftKey && e.buttons === 2) {
-            if (this.props.isTopMost) {
-                this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
-            } else {
-                CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
-            }
-            e.stopPropagation();
-        } else {
-            let maxdoc = this.props.Document.GetT(KeyStore.MaximizedDoc, Document);
-            if (this.active ||
-                maxdoc instanceof Document // bcz: need a better way of allowing a document to handle pointer events when its not active (ie. be a top-level widget)
-            ) {
-                e.stopPropagation();
-                document.removeEventListener("pointermove", this.onPointerMove);
-                document.addEventListener("pointermove", this.onPointerMove);
-                document.removeEventListener("pointerup", this.onPointerUp);
-                document.addEventListener("pointerup", this.onPointerUp);
-            }
-        }
-    }
-
+    @action
     componentDidMount() {
         if (this._mainCont.current) {
             this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
                 handlers: { drop: this.drop.bind(this) }
             });
         }
-        runInAction(() => DocumentManager.Instance.DocumentViews.push(this));
+        DocumentManager.Instance.DocumentViews.push(this);
     }
-
+    @action
     componentDidUpdate() {
         if (this._dropDisposer) {
             this._dropDisposer();
@@ -142,12 +114,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             });
         }
     }
-
+    @action
     componentWillUnmount() {
         if (this._dropDisposer) {
             this._dropDisposer();
         }
-        runInAction(() => DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1));
+        DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
+    }
+
+    stopPropagation = (e: React.SyntheticEvent) => {
+        e.stopPropagation();
     }
 
     startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
@@ -168,43 +144,51 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
     }
 
-    onPointerMove = (e: PointerEvent): void => {
-        if (e.cancelBubble) {
+    onPointerDown = (e: React.PointerEvent): void => {
+        this._downX = e.clientX;
+        this._downY = e.clientY;
+        if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
             return;
         }
-        if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+        if (e.shiftKey && e.buttons === 2) {
+            if (this.props.isTopMost) {
+                this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
+            } else {
+                CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
+            }
+            e.stopPropagation();
+        } else if (this.active) {
             document.removeEventListener("pointermove", this.onPointerMove);
+            document.addEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
-            if (!e.altKey && (!this.topMost || e.buttons === 2)) {
-                this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
+            document.addEventListener("pointerup", this.onPointerUp);
+        }
+    }
+    onPointerMove = (e: PointerEvent): void => {
+        if (!e.cancelBubble) {
+            if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
+                document.removeEventListener("pointermove", this.onPointerMove);
+                document.removeEventListener("pointerup", this.onPointerUp);
+                if (!e.altKey && (!this.topMost || e.buttons === 2)) {
+                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
+                }
             }
+            e.stopPropagation();
+            e.preventDefault();
         }
-        e.stopPropagation();
-        e.preventDefault();
     }
     onPointerUp = (e: PointerEvent): void => {
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
-        e.stopPropagation();
         if (!SelectionManager.IsSelected(this) && e.button !== 2 &&
             Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
-            this.props.Document.GetTAsync(KeyStore.MaximizedDoc, Document).then(maxdoc => {
-                if (maxdoc instanceof Document) {   // bcz: need a better way to associate behaviors with click events on widget-documents
-                    this.props.addDocument && this.props.addDocument(maxdoc, false);
-                    this.toggleIcon();
-                } else
-                    SelectionManager.SelectDoc(this, e.ctrlKey);
-            });
+            SelectionManager.SelectDoc(this, e.ctrlKey);
         }
     }
-    stopPropagation = (e: React.SyntheticEvent) => {
-        e.stopPropagation();
-    }
 
     deleteClicked = (): void => {
         this.props.removeDocument && this.props.removeDocument(this.props.Document);
     }
-
     fieldsClicked = (e: React.MouseEvent): void => {
         let kvp = Documents.KVPDocument(this.props.Document, { width: 300, height: 300 });
         CollectionDockingView.Instance.AddRightSplit(kvp);
@@ -215,7 +199,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked });
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
     }
-
     closeFullScreenClicked = (e: React.MouseEvent): void => {
         CollectionDockingView.Instance.CloseFullScreen();
         ContextMenu.Instance.clearItems();
@@ -223,92 +206,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
     }
 
-    @action createIcon = (layoutString: string): Document => {
-        let iconDoc = Documents.IconDocument(layoutString);
-        iconDoc.SetText(KeyStore.Title, "ICON" + this.props.Document.Title)
-        iconDoc.SetBoolean(KeyStore.IsMinimized, false);
-        iconDoc.SetNumber(KeyStore.NativeWidth, 0);
-        iconDoc.SetNumber(KeyStore.NativeHeight, 0);
-        iconDoc.SetNumber(KeyStore.X, this.props.Document.GetNumber(KeyStore.X, 0));
-        iconDoc.SetNumber(KeyStore.Y, this.props.Document.GetNumber(KeyStore.Y, 0) - 24);
-        iconDoc.Set(KeyStore.Prototype, this.props.Document);
-        iconDoc.Set(KeyStore.MaximizedDoc, this.props.Document);
-        this.props.Document.Set(KeyStore.MinimizedDoc, iconDoc);
-        this.props.addDocument && this.props.addDocument(iconDoc, false);
-        return iconDoc;
-    }
-
-    animateBetweenIcon(icon: number[], targ: number[], width: number, height: number, stime: number, target: Document, maximizing: boolean) {
-        setTimeout(() => {
-            let now = Date.now();
-            let progress = Math.min(1, (now - stime) / 200);
-            let pval = maximizing ?
-                [icon[0] + (targ[0] - icon[0]) * progress, icon[1] + (targ[1] - icon[1]) * progress] :
-                [targ[0] + (icon[0] - targ[0]) * progress, targ[1] + (icon[1] - targ[1]) * progress];
-            target.SetNumber(KeyStore.Width, maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress);
-            target.SetNumber(KeyStore.Height, maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress);
-            target.SetNumber(KeyStore.X, pval[0]);
-            target.SetNumber(KeyStore.Y, pval[1]);
-            if (now < stime + 200) {
-                this.animateBetweenIcon(icon, targ, width, height, stime, target, maximizing);
-            }
-            else {
-                if (!maximizing) {
-                    target.SetBoolean(KeyStore.IsMinimized, true);
-                    target.SetNumber(KeyStore.X, targ[0]);
-                    target.SetNumber(KeyStore.Y, targ[1]);
-                    target.SetNumber(KeyStore.Width, width);
-                    target.SetNumber(KeyStore.Height, height);
-                }
-                DocumentView._incompleteAnimations.set(target.Id, false);
-            }
-        },
-            2);
-    }
-
-    @action
-    public toggleIcon = async (): Promise<void> => {
-        SelectionManager.DeselectAll();
-        let isMinimized: boolean | undefined;
-        let minDoc = await this.props.Document.GetTAsync(KeyStore.MinimizedDoc, Document);
-        if (!minDoc) return;
-        let minimizedDocSet = await minDoc.GetTAsync(KeyStore.LinkTags, ListField);
-        if (!minimizedDocSet) return;
-        minimizedDocSet.Data.map(async minimizedDoc => {
-            if (minimizedDoc instanceof Document) {
-                this.props.addDocument && this.props.addDocument(minimizedDoc, false);
-                let maximizedDoc = await minimizedDoc.GetTAsync(KeyStore.MaximizedDoc, Document);
-                if (maximizedDoc instanceof Document && !DocumentView._incompleteAnimations.get(maximizedDoc.Id)) {
-                    DocumentView._incompleteAnimations.set(maximizedDoc.Id, true);
-                    isMinimized = isMinimized === undefined ? maximizedDoc.GetBoolean(KeyStore.IsMinimized, false) : isMinimized;
-                    maximizedDoc.SetBoolean(KeyStore.IsMinimized, false);
-                    let minx = await minimizedDoc.GetTAsync(KeyStore.X, NumberField);
-                    let miny = await minimizedDoc.GetTAsync(KeyStore.Y, NumberField);
-                    let maxx = await maximizedDoc.GetTAsync(KeyStore.X, NumberField);
-                    let maxy = await maximizedDoc.GetTAsync(KeyStore.Y, NumberField);
-                    let maxw = await maximizedDoc.GetTAsync(KeyStore.Width, NumberField);
-                    let maxh = await maximizedDoc.GetTAsync(KeyStore.Height, NumberField);
-                    if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
-                        maxw !== undefined && maxh !== undefined)
-                        this.animateBetweenIcon(
-                            [minx.Data, miny.Data], [maxx.Data, maxy.Data], maxw.Data, maxh.Data,
-                            Date.now(), maximizedDoc, isMinimized);
-                }
-
-            }
-        })
-    }
-
-    @action
-    public getIconDoc = async (): Promise<Document | undefined> => {
-        return await this.props.Document.GetTAsync(KeyStore.MinimizedDoc, Document).then(async mindoc =>
-            mindoc ? mindoc :
-                await this.props.Document.GetTAsync(KeyStore.BackgroundLayout, TextField).then(async field =>
-                    (field instanceof TextField) ? this.createIcon(field.Data) :
-                        await this.props.Document.GetTAsync(KeyStore.Layout, TextField).then(field =>
-                            (field instanceof TextField) ? this.createIcon(field.Data) : undefined)));
-    }
-
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
@@ -353,6 +250,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
     }
 
+    @action
     onDrop = (e: React.DragEvent) => {
         let text = e.dataTransfer.getData("text/plain");
         if (!e.isDefaultPrevented() && text && text.startsWith("<div")) {
@@ -389,12 +287,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     isSelected = () => SelectionManager.IsSelected(this);
     select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
-
     @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); }
     @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); }
     @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />); }
 
-
     render() {
         var scaling = this.props.ContentScaling();
         var nativeHeight = this.nativeHeight > 0 ? this.nativeHeight.toString() + "px" : "100%";
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5d9aefd61..a16fd5007 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -16,7 +16,7 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import React = require("react");
 import { InkingControl } from '../InkingControl';
-import { InkTool } from '../../../fields/InkField';
+import { NumberField } from '../../../fields/NumberField';
 
 @observer
 export class ImageBox extends React.Component<FieldViewProps> {
@@ -42,7 +42,8 @@ export class ImageBox extends React.Component<FieldViewProps> {
         var w = this._imgRef.current!.naturalWidth;
         if (this._photoIndex === 0) {
             this.props.Document.SetNumber(KeyStore.NativeHeight, this.props.Document.GetNumber(KeyStore.NativeWidth, 0) * h / w);
-            this.props.Document.SetNumber(KeyStore.Height, this.props.Document.Width() * h / w);
+            this.props.Document.GetTAsync(KeyStore.Width, NumberField, field =>
+                field && this.props.Document.SetNumber(KeyStore.Height, field.Data * h / w));
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From 27f505949ae47c3c5f83633115b9d238936d4003 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 23 Apr 2019 22:39:41 -0400
Subject: cleaned up events a bit

---
 src/client/views/PreviewCursor.tsx                 | 35 +++++++++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  8 +---
 .../collections/collectionFreeForm/MarqueeView.tsx | 55 +++++++---------------
 src/client/views/nodes/DocumentView.tsx            |  7 +--
 4 files changed, 48 insertions(+), 57 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 5ece034fb..39b6c6544 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -7,32 +7,45 @@ import "./PreviewCursor.scss";
 @observer
 export class PreviewCursor extends React.Component<{}> {
     private _prompt = React.createRef<HTMLDivElement>();
+    static _onKeyPress?: (e: KeyboardEvent) => void;
+    @observable static _clickPoint = [0, 0];
+    @observable public static Visible = false;
     //when focus is lost, this will remove the preview cursor
     @action onBlur = (): void => {
         PreviewCursor.Visible = false;
-        PreviewCursor.hide();
     }
 
-    @observable static clickPoint = [0, 0];
-    @observable public static Visible = false;
-    @observable public static hide = () => { };
+    constructor(props: any) {
+        super(props);
+        document.addEventListener("keypress", this.onKeyPress)
+    }
+
+    @action
+    onKeyPress = (e: KeyboardEvent) => {
+        // Mixing events between React and Native is finicky.  In FormattedTextBox, we set the
+        // DASHFormattedTextBoxHandled flag when a text box consumes a key press so that we can ignore
+        // the keyPress here.
+        //if not these keys, make a textbox if preview cursor is active!
+        if (!e.ctrlKey && !e.altKey && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
+            PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e);
+        }
+        PreviewCursor.Visible = false;
+    }
     @action
-    public static Show(hide: any, x: number, y: number) {
-        if (this.hide)
-            this.hide();
-        this.clickPoint = [x, y];
-        this.hide = hide;
+    public static Show(x: number, y: number, onKeyPress: (e: KeyboardEvent) => void) {
+        this._clickPoint = [x, y];
+        this._onKeyPress = onKeyPress;
         setTimeout(action(() => this.Visible = true), (1));
     }
     render() {
-        if (!PreviewCursor.clickPoint) {
+        if (!PreviewCursor._clickPoint) {
             return (null);
         }
         if (PreviewCursor.Visible && this._prompt.current) {
             this._prompt.current.focus();
         }
         return <div className="previewCursor" id="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={this._prompt}
-            style={{ transform: `translate(${PreviewCursor.clickPoint[0]}px, ${PreviewCursor.clickPoint[1]}px)`, opacity: PreviewCursor.Visible ? 1 : 0 }}>
+            style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)`, opacity: PreviewCursor.Visible ? 1 : 0 }}>
             I
         </div >;
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 56d54696d..7492d8744 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -118,19 +118,15 @@ export class CollectionFreeFormView extends CollectionSubView {
                 (e.button === 0 && e.altKey)) && (childSelected || this.props.active()))) ||
             (!CollectionFreeFormView.RIGHT_BTN_DRAG &&
                 ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())))) {
-            document.removeEventListener("pointermove", this.onPointerMove);
+            this.cleanupInteractions();
             document.addEventListener("pointermove", this.onPointerMove);
-            document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointerup", this.onPointerUp);
             this._lastX = e.pageX;
             this._lastY = e.pageY;
         }
     }
 
-    @action
     onPointerUp = (e: PointerEvent): void => {
-        e.stopPropagation();
-
         this.cleanupInteractions();
     }
 
@@ -164,7 +160,7 @@ export class CollectionFreeFormView extends CollectionSubView {
             this.setPan(x - dx, y - dy);
             this._lastX = e.pageX;
             this._lastY = e.pageY;
-            e.stopPropagation();
+            e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
             e.preventDefault();
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 32633a1ed..623643238 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -32,9 +32,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     @observable _lastY: number = 0;
     @observable _downX: number = 0;
     @observable _downY: number = 0;
-    @observable _used: boolean = false;
     @observable _visible: boolean = false;
-    _showOnUp: boolean = false;
     static DRAG_THRESHOLD = 4;
 
     @action
@@ -42,8 +40,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         if (all) {
             document.removeEventListener("pointermove", this.onPointerMove, true);
             document.removeEventListener("pointerup", this.onPointerUp, true);
-        } else {
-            this._used = true;
         }
         document.removeEventListener("keydown", this.marqueeCommand, true);
         this._visible = false;
@@ -51,32 +47,19 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @action
     onKeyPress = (e: KeyboardEvent) => {
-        // Mixing events between React and Native is finicky.  In FormattedTextBox, we set the
-        // DASHFormattedTextBoxHandled flag when a text box consumes a key press so that we can ignore
-        // the keyPress here.
-        //if not these keys, make a textbox if preview cursor is active!
-        if (!e.ctrlKey && !e.altKey && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
-            //make textbox and add it to this collection
-            let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
-            let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "typed text" });
-            this.props.addLiveTextDocument(newBox);
-            PreviewCursor.Visible = false;
-            e.stopPropagation();
-        }
-    }
-    hideCursor = () => {
-        document.removeEventListener("keypress", this.onKeyPress, false);
+        //make textbox and add it to this collection
+        let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
+        let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "typed text" });
+        this.props.addLiveTextDocument(newBox);
+        e.stopPropagation();
     }
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = this._lastX = e.pageX;
         this._downY = this._lastY = e.pageY;
-
-        document.removeEventListener("keypress", this.onKeyPress, false);
+        PreviewCursor.Visible = false;
         if ((CollectionFreeFormView.RIGHT_BTN_DRAG && e.button === 0 && !e.altKey && !e.metaKey && this.props.container.props.active()) ||
             (!CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && this.props.container.props.active())) {
-            this._used = false;
-            this._showOnUp = true;
             document.addEventListener("pointermove", this.onPointerMove, true);
             document.addEventListener("pointerup", this.onPointerUp, true);
             document.addEventListener("keydown", this.marqueeCommand, true);
@@ -91,14 +74,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
         this._lastX = e.pageX;
         this._lastY = e.pageY;
         if (!e.cancelBubble) {
-            if (Math.abs(this._downX - e.clientX) > 4 || Math.abs(this._downY - e.clientY) > 4) {
-                this._showOnUp = false;
-                PreviewCursor.Visible = false;
-            }
-            if (!this._used &&
-                (!CollectionFreeFormView.RIGHT_BTN_DRAG && (e.buttons === 2 || (e.buttons == 1 && e.altKey))) ||
-                (CollectionFreeFormView.RIGHT_BTN_DRAG && e.buttons === 1 && !e.altKey && !e.metaKey) &&
-                (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD)) {
+            if (Math.abs(this._lastX - this._downX) > MarqueeView.DRAG_THRESHOLD ||
+                Math.abs(this._lastY - this._downY) > MarqueeView.DRAG_THRESHOLD) {
                 this._visible = true;
                 e.stopPropagation();
                 e.preventDefault();
@@ -110,24 +87,28 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
 
     @action
     onPointerUp = (e: PointerEvent): void => {
-        this.cleanupInteractions(true);
-        this._visible = false;
-        if (!this._showOnUp && (CollectionFreeFormView.RIGHT_BTN_DRAG && e.button === 0 && !e.altKey && !e.metaKey) ||
-            (!CollectionFreeFormView.RIGHT_BTN_DRAG && ((e.button === 0 && e.altKey) || e.button === 2))) {
+        if (this._visible) {
             let mselect = this.marqueeSelect();
             if (!e.shiftKey) {
                 SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
             }
             this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
         }
+        this.cleanupInteractions(true);
         if (e.altKey)
             e.preventDefault();
     }
 
     @action
     onClick = (e: React.MouseEvent): void => {
-        PreviewCursor.Show(this.hideCursor, e.clientX, e.clientY);
-        document.addEventListener("keypress", this.onKeyPress, false);
+        if (Math.abs(e.clientX - this._downX) < MarqueeView.DRAG_THRESHOLD &&
+            Math.abs(e.clientY - this._downY) < MarqueeView.DRAG_THRESHOLD) {
+            PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress);
+            // let the DocumentView stopPropagation of this event when it selects this document
+        } else {  // why do we get a click event when the cursor have moved a big distance?
+            // let's cut it off here so no one else has to deal with it.
+            e.stopPropagation();
+        }
     }
 
     intersectRect(r1: { left: number, top: number, width: number, height: number },
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6784ac310..b3b9f6e51 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -23,6 +23,7 @@ import "./DocumentView.scss";
 import React = require("react");
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView";
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
@@ -146,8 +147,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     onClick = (e: React.MouseEvent): void => {
-        console.log("click = " + this.props.Document.Title);
-        if (CurrentUserUtils.MainDocId != this.props.Document.Id) {
+        if (CurrentUserUtils.MainDocId != this.props.Document.Id &&
+            (Math.abs(e.clientX - this._downX) < MarqueeView.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < MarqueeView.DRAG_THRESHOLD)) {
             SelectionManager.SelectDoc(this, e.ctrlKey);
         }
         e.stopPropagation();
@@ -182,7 +183,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                     this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey);
                 }
             }
-            e.stopPropagation();
+            e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
             e.preventDefault();
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 2e2377aaee0801ab20134b54e5105815c59f38e8 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 24 Apr 2019 20:50:53 -0400
Subject: Freeform view done

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 105 ++++++-------
 src/new_fields/Types.ts                            |   2 +-
 src/server/ServerUtil.ts                           |  62 --------
 test/test.ts                                       | 166 ++-------------------
 4 files changed, 69 insertions(+), 266 deletions(-)
 delete mode 100644 src/server/ServerUtil.ts

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ee7d65d23..3a9c9780b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -19,7 +19,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
-import { createSchema, makeInterface } from "../../../../new_fields/Schema";
+import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
 import { Doc, Id } from "../../../../new_fields/Doc";
 import { FieldValue, Cast } from "../../../../new_fields/Types";
 import { pageSchema } from "../../nodes/ImageBox";
@@ -83,19 +83,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset);
             if (de.data.droppedDocuments.length) {
                 let dragDoc = de.data.droppedDocuments[0];
-                let dropX = dragDoc.GetNumber(KeyStore.X, 0);
-                let dropY = dragDoc.GetNumber(KeyStore.Y, 0);
+                let dropX = Cast(dragDoc.x, "number", 0);
+                let dropY = Cast(dragDoc.y, "number", 0);
                 de.data.droppedDocuments.map(d => {
-                    d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0)) - dropX);
-                    d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0)) - dropY);
-                    if (!d.GetBoolean(KeyStore.IsMinimized, false)) {
-                        if (!d.GetNumber(KeyStore.Width, 0)) {
-                            d.SetNumber(KeyStore.Width, 300);
+                    d.x = x + Cast(d.x, "number", 0) - dropX;
+                    d.y = y + Cast(d.y, "number", 0) - dropY;
+                    if (!Cast(d.isMinimized, "boolean", false)) {
+                        if (!Cast(d.width, "number", 0)) {
+                            d.width = 300;
                         }
-                        if (!d.GetNumber(KeyStore.Height, 0)) {
-                            let nw = d.GetNumber(KeyStore.NativeWidth, 0);
-                            let nh = d.GetNumber(KeyStore.NativeHeight, 0);
-                            d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300);
+                        if (!Cast(d.height, "number", 0)) {
+                            let nw = Cast(d.nativeWidth, "number", 0);
+                            let nh = Cast(d.nativeHeight, "number", 0);
+                            d.height = nw && nh ? nh / nw * Cast(d.width, "number", 0) : 300;
                         }
                     }
                     this.bringToFront(d);
@@ -115,7 +115,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
-        let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
+        let childSelected = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), [] as Doc[]).filter(doc => doc).reduce((childSelected, doc) => {
             var dv = DocumentManager.Instance.getDocumentView(doc);
             return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
         }, false);
@@ -139,20 +139,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @action
     onPointerMove = (e: PointerEvent): void => {
         if (!e.cancelBubble) {
-            let x = this.props.Document.GetNumber(KeyStore.PanX, 0);
-            let y = this.props.Document.GetNumber(KeyStore.PanY, 0);
-            let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]);
+            let x = Cast(this.props.Document.panX, "number", 0);
+            let y = Cast(this.props.Document.panY, "number", 0);
+            let docs = this.children || [];
             let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
             if (!this.isAnnotationOverlay) {
-                let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0;
-                let maxx = docs.length ? docs[0].Width() + minx : minx;
-                let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0;
-                let maxy = docs.length ? docs[0].Height() + miny : miny;
+                let minx = docs.length ? Cast(docs[0].x, "number", 0) : 0;
+                let maxx = docs.length ? Cast(docs[0].width, "number", 0) + minx : minx;
+                let miny = docs.length ? Cast(docs[0].y, "number", 0) : 0;
+                let maxy = docs.length ? Cast(docs[0].height, "number", 0) + miny : miny;
                 let ranges = docs.filter(doc => doc).reduce((range, doc) => {
-                    let x = doc.GetNumber(KeyStore.X, 0);
-                    let xe = x + doc.GetNumber(KeyStore.Width, 0);
-                    let y = doc.GetNumber(KeyStore.Y, 0);
-                    let ye = y + doc.GetNumber(KeyStore.Height, 0);
+                    let x = Cast(doc.x, "number", 0);
+                    let xe = x + Cast(doc.width, "number", 0);
+                    let y = Cast(doc.y, "number", 0);
+                    let ye = y + Cast(doc.height, "number", 0);
                     return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
                     [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
                 }, [[minx, maxx], [miny, maxy]]);
@@ -176,10 +176,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         // if (!this.props.active()) {
         //     return;
         // }
-        let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => {
+        let childSelected = (this.children || []).filter(doc => doc).some(doc => {
             var dv = DocumentManager.Instance.getDocumentView(doc);
-            return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false);
-        }, false);
+            return dv && SelectionManager.IsSelected(dv) ? true : false;
+        });
         if (!this.props.isSelected() && !childSelected && !this.props.isTopMost) {
             return;
         }
@@ -188,8 +188,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
         if (e.ctrlKey) {
             let deltaScale = (1 - (e.deltaY / coefficient));
-            this.props.Document.SetNumber(KeyStore.NativeWidth, this.nativeWidth * deltaScale);
-            this.props.Document.SetNumber(KeyStore.NativeHeight, this.nativeHeight * deltaScale);
+            this.props.Document.nativeWidth = this.nativeWidth * deltaScale;
+            this.props.Document.nativeHeight = this.nativeHeight * deltaScale;
             e.stopPropagation();
             e.preventDefault();
         } else {
@@ -204,7 +204,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
             let safeScale = Math.abs(localTransform.Scale);
-            this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale));
+            this.props.Document.scale = Math.abs(safeScale);
             this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
             e.stopPropagation();
         }
@@ -216,8 +216,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         var scale = this.getLocalTransform().inverse().Scale;
         const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
         const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
-        this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX);
-        this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY);
+        this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+        this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
     }
 
     @action
@@ -230,23 +230,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     @action
-    bringToFront(doc: Document) {
-        this.props.Document.GetList(this.props.fieldKey, [] as Document[]).slice().sort((doc1, doc2) => {
+    bringToFront(doc: Doc) {
+        (this.children || []).slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
             if (doc2 === doc) return -1;
-            return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0);
-        }).map((doc, index) => doc.SetNumber(KeyStore.ZIndex, index + 1));
+            return Cast(doc1.zIndex, "number", 0) - Cast(doc2.zIndex, "number", 0);
+        }).forEach((doc, index) => doc.zIndex = index + 1);
         return doc;
     }
 
-    focusDocument = (doc: Document) => {
+    focusDocument = (doc: Doc) => {
         this.setPan(
-            doc.GetNumber(KeyStore.X, 0) + doc.Width() / 2,
-            doc.GetNumber(KeyStore.Y, 0) + doc.Height() / 2);
+            Cast(doc.x, "number", 0) + Cast(doc.width, "number", 0) / 2,
+            Cast(doc.y, "number", 0) + Cast(doc.height, "number", 0) / 2);
         this.props.focus(this.props.Document);
     }
 
-    getDocumentViewProps(document: Document): DocumentViewProps {
+    getDocumentViewProps(document: Doc): DocumentViewProps {
         return {
             Document: document,
             addDocument: this.props.addDocument,
@@ -255,8 +255,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
             selectOnLoad: document.Id === this._selectOnLoaded,
-            PanelWidth: document.Width,
-            PanelHeight: document.Height,
+            PanelWidth: () => Cast(document.width, "number", 0),//TODO Types These are inline functions
+            PanelHeight: () => Cast(document.height, "number", 0),
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
@@ -267,13 +267,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @computed
     get views() {
-        var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1);
-        let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => {
-            var page = doc.GetNumber(KeyStore.Page, -1);
+        let curPage = FieldValue(this.Document.curPage, -1);
+        let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
+            var page = Cast(doc.page, "number", -1);
             if (page === curPage || page === -1) {
-                let minim = doc.GetT(KeyStore.IsMinimized, BooleanField);
-                if (minim === undefined || (minim && !minim.Data))
-                    prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />);
+                let minim = Cast(doc.isMinimized, "boolean");
+                if (minim === undefined || !minim) {
+                    prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
+                }
             }
             return prev;
         }, [] as JSX.Element[]);
@@ -316,9 +317,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 @observer
 class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
     @computed get overlayView() {
-        let overlayLayout = this.props.Document.GetText(KeyStore.OverlayLayout, "");
+        let overlayLayout = Cast(this.props.Document.overlayLayout, "string", "");
         return !overlayLayout ? (null) :
-            (<DocumentContentsView {...this.props} layoutKey={KeyStore.OverlayLayout}
+            (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
                 isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
     render() {
@@ -329,9 +330,9 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
 @observer
 class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps> {
     @computed get backgroundView() {
-        let backgroundLayout = this.props.Document.GetText(KeyStore.BackgroundLayout, "");
+        let backgroundLayout = Cast(this.props.Document.backgroundLayout, "string", "");
         return !backgroundLayout ? (null) :
-            (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout}
+            (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
                 isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
     render() {
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 55083765a..4ad8dcade 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -38,7 +38,7 @@ export interface Interface {
 }
 
 export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal: ToType<T>): ToType<T>;
+export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal: WithoutList<ToType<T>>): WithoutList<ToType<T>>;
 export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal?: ToType<T>): FieldResult<ToType<T>> | undefined {
     if (field instanceof Promise) {
         return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal;
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
deleted file mode 100644
index 79ca5e55d..000000000
--- a/src/server/ServerUtil.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { HistogramField } from "../client/northstar/dash-fields/HistogramField";
-import { AudioField } from "../fields/AudioField";
-import { BooleanField } from "../fields/BooleanField";
-import { HtmlField } from "../fields/HtmlField";
-import { InkField } from "../fields/InkField";
-import { PDFField } from "../fields/PDFField";
-import { ScriptField } from "../fields/ScriptField";
-import { TupleField } from "../fields/TupleField";
-import { VideoField } from "../fields/VideoField";
-import { WebField } from "../fields/WebField";
-import { Utils } from "../Utils";
-import { Document } from "./../fields/Document";
-import { Field } from "./../fields/Field";
-import { ImageField } from "./../fields/ImageField";
-import { Key } from "./../fields/Key";
-import { ListField } from "./../fields/ListField";
-import { NumberField } from "./../fields/NumberField";
-import { RichTextField } from "./../fields/RichTextField";
-import { TextField } from "./../fields/TextField";
-import { Transferable, Types } from "./Message";
-import { IconField } from "../fields/IconFIeld";
-
-export class ServerUtils {
-    public static prepend(extension: string): string {
-        return window.location.origin + extension;
-    }
-
-    public static FromJson(json: Transferable): Field {
-
-        if (!(json.data !== undefined && json.id && json.type !== undefined)) {
-            console.log(
-                "how did you manage to get an object that doesn't have a data or an id?"
-            );
-            return new TextField("Something to fill the space", Utils.GenerateGuid());
-        }
-
-        switch (json.type) {
-            case Types.Boolean: return new BooleanField(json.data, json.id, false);
-            case Types.Number: return new NumberField(json.data, json.id, false);
-            case Types.Text: return new TextField(json.data, json.id, false);
-            case Types.Icon: return new IconField(json.data, json.id, false);
-            case Types.Html: return new HtmlField(json.data, json.id, false);
-            case Types.Web: return new WebField(new URL(json.data), json.id, false);
-            case Types.RichText: return new RichTextField(json.data, json.id, false);
-            case Types.Key: return new Key(json.data, json.id, false);
-            case Types.Image: return new ImageField(new URL(json.data), json.id, false);
-            case Types.HistogramOp: return HistogramField.FromJson(json.id, json.data);
-            case Types.PDF: return new PDFField(new URL(json.data), json.id, false);
-            case Types.List: return ListField.FromJson(json.id, json.data);
-            case Types.Script: return ScriptField.FromJson(json.id, json.data);
-            case Types.Audio: return new AudioField(new URL(json.data), json.id, false);
-            case Types.Video: return new VideoField(new URL(json.data), json.id, false);
-            case Types.Tuple: return new TupleField(json.data, json.id, false);
-            case Types.Ink: return InkField.FromJson(json.id, json.data);
-            case Types.Document: return Document.FromJson(json.data, json.id, false);
-            default:
-                throw Error(
-                    "Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here"
-                );
-        }
-    }
-}
diff --git a/test/test.ts b/test/test.ts
index 16cace026..91dc43379 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -1,171 +1,35 @@
-import { NumberField } from "../src/fields/NumberField";
 import { expect } from 'chai';
 import 'mocha';
-import { Key } from "../src/fields/Key";
-import { Document } from "../src/fields/Document";
 import { autorun, reaction } from "mobx";
-import { DocumentReference } from "../src/fields/DocumentReference";
-import { TextField } from "../src/fields/TextField";
-import { Field, FieldWaiting } from "../src/fields/Field";
-
-describe('Number Controller', () => {
-    it('Should be constructable', () => {
-        const numController = new NumberField(15);
-        expect(numController.Data).to.equal(15);
-    });
-
-    it('Should update', () => {
-        const numController = new NumberField(15);
-        let ran = false;
-        reaction(() => numController.Data, (data) => { ran = true; });
-        expect(ran).to.equal(false);
-        numController.Data = 5;
-        expect(ran).to.equal(true);
-    });
-});
+import { Doc } from '../src/new_fields/Doc';
+import { Cast } from '../src/new_fields/Types';
 
 describe("Document", () => {
     it('should hold fields', () => {
-        let key = new Key("Test");
-        let key2 = new Key("Test2");
-        let field = new NumberField(15);
-        let doc = new Document();
-        doc.Set(key, field);
-        let getField = doc.GetT(key, NumberField);
-        let getField2 = doc.GetT(key2, NumberField);
+        let key = "Test";
+        let key2 = "Test2";
+        let field = 15;
+        let doc = new Doc();
+        doc[key] = field;
+        let getField = Cast(doc[key], "number");
+        let getField2 = Cast(doc[key2], "number");
         expect(getField).to.equal(field);
         expect(getField2).to.equal(undefined);
     });
 
     it('should update', () => {
-        let doc = new Document();
-        let key = new Key("Test");
-        let key2 = new Key("Test2");
+        let doc = new Doc();
+        let key = "Test";
+        let key2 = "Test2";
         let ran = false;
-        reaction(() => doc.Get(key), (field) => { ran = true; });
+        reaction(() => doc[key], (field) => { ran = true; });
         expect(ran).to.equal(false);
 
-        doc.Set(key2, new NumberField(4));
+        doc[key2] = 4;
         expect(ran).to.equal(false);
 
-        doc.Set(key, new NumberField(5));
+        doc[key] = 5;
 
         expect(ran).to.equal(true);
     });
 });
-
-describe("Reference", () => {
-    it('should dereference', () => {
-        let doc = new Document();
-        let doc2 = new Document();
-        const key = new Key("test");
-        const key2 = new Key("test2");
-
-        const numCont = new NumberField(55);
-        doc.Set(key, numCont);
-        let ref = new DocumentReference(doc, key);
-        let ref2 = new DocumentReference(doc, key2);
-        doc2.Set(key2, ref);
-
-        let ref3 = new DocumentReference(doc2, key2);
-        let ref4 = new DocumentReference(doc2, key);
-
-        expect(ref.Dereference()).to.equal(numCont);
-        expect(ref.DereferenceToRoot()).to.equal(numCont);
-        expect(ref2.Dereference()).to.equal(undefined);
-        expect(ref2.DereferenceToRoot()).to.equal(undefined);
-        expect(ref3.Dereference()).to.equal(ref);
-        expect(ref3.DereferenceToRoot()).to.equal(numCont);
-        expect(ref4.Dereference()).to.equal(undefined);
-        expect(ref4.DereferenceToRoot()).to.equal(undefined);
-    });
-
-    it('should work with prototypes', () => {
-        let doc = new Document;
-        let doc2 = doc.MakeDelegate();
-        let key = new Key("test");
-        expect(doc.Get(key)).to.equal(undefined);
-        expect(doc2.Get(key)).to.equal(undefined);
-        let num = new NumberField(55);
-        let num2 = new NumberField(56);
-
-        doc.Set(key, num);
-        expect(doc.Get(key)).to.equal(num);
-        expect(doc2.Get(key)).to.equal(num);
-
-        doc2.Set(key, num2);
-        expect(doc.Get(key)).to.equal(num);
-        expect(doc2.Get(key)).to.equal(num2);
-    });
-
-    it('should update through layers', () => {
-        let doc = new Document();
-        let doc2 = new Document();
-        let doc3 = new Document();
-        const key = new Key("test");
-        const key2 = new Key("test2");
-        const key3 = new Key("test3");
-
-        const numCont = new NumberField(55);
-        doc.Set(key, numCont);
-        const ref = new DocumentReference(doc, key);
-        doc2.Set(key2, ref);
-        const ref3 = new DocumentReference(doc2, key2);
-        doc3.Set(key3, ref3);
-
-        let ran = false;
-        reaction(() => {
-            let field = (<Field>(<Field>doc3.Get(key3)).DereferenceToRoot()).GetValue();
-            return field;
-        }, (field) => {
-            ran = true;
-        });
-        expect(ran).to.equal(false);
-
-        numCont.Data = 44;
-        expect(ran).to.equal(true);
-        ran = false;
-
-        doc.Set(key, new NumberField(33));
-        expect(ran).to.equal(true);
-        ran = false;
-
-        doc.Set(key2, new NumberField(4));
-        expect(ran).to.equal(false);
-
-        doc2.Set(key2, new TextField("hello"));
-        expect(ran).to.equal(true);
-        ran = false;
-
-        doc3.Set(key3, new TextField("world"));
-        expect(ran).to.equal(true);
-        ran = false;
-    });
-
-    it('should update with prototypes', () => {
-        let doc = new Document();
-        let doc2 = doc.MakeDelegate();
-        const key = new Key("test");
-
-        const numCont = new NumberField(55);
-
-        let ran = false;
-        reaction(() => {
-            let field = doc2.GetT(key, NumberField);
-            if (field && field !== FieldWaiting) {
-                return field.Data;
-            }
-            return undefined;
-        }, (field) => {
-            ran = true;
-        });
-        expect(ran).to.equal(false);
-
-        doc.Set(key, numCont);
-        expect(ran).to.equal(true);
-
-        ran = false;
-        numCont.Data = 1;
-        expect(ran).to.equal(true);
-    });
-});
\ 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/collections/collectionFreeForm/CollectionFreeFormView.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/collections/collectionFreeForm/CollectionFreeFormView.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/collections/collectionFreeForm/CollectionFreeFormView.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 b2d9133f2fdb6dd2af3b74c4dd9de49145611b1b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 26 Apr 2019 22:08:20 -0400
Subject: fixed previewcursor appearing on select. adjusted rounded border
 text.

---
 src/client/views/PreviewCursor.tsx                            |  2 +-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx |  2 +-
 .../views/collections/collectionFreeForm/MarqueeView.tsx      |  6 +++++-
 src/client/views/nodes/DocumentView.tsx                       |  1 +
 src/client/views/nodes/FormattedTextBox.scss                  | 11 ++++++++++-
 src/client/views/nodes/FormattedTextBox.tsx                   | 11 +++++++----
 6 files changed, 25 insertions(+), 8 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 2e63f751b..4359ba093 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -27,7 +27,7 @@ export class PreviewCursor extends React.Component<{}> {
         // the keyPress here.
         //if not these keys, make a textbox if preview cursor is active!
         if (e.key.startsWith("F") && !e.key.endsWith("F")) {
-        } else if (e.key != "Alt" && e.key != "Shift" && e.key != "Meta" && e.key != "Control" && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
+        } else if (e.key != "Escape" && e.key != "Alt" && e.key != "Shift" && e.key != "Meta" && e.key != "Control" && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
             if ((!e.ctrlKey && !e.metaKey) || e.key === "v") {
                 PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e);
                 PreviewCursor.Visible = false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 405594eb1..1a953006a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -295,7 +295,7 @@ export class CollectionFreeFormView extends CollectionSubView {
                         {this.props.Document.Title}
                     </text>
                 </svg> */}
-                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+                <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
                     addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
                     <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index c6681e014..0e2ac0a80 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -24,6 +24,7 @@ interface MarqueeViewProps {
     selectDocuments: (docs: Document[]) => void;
     removeDocument: (doc: Document) => boolean;
     addLiveTextDocument: (doc: Document) => void;
+    isSelected: () => boolean;
 }
 
 @observer
@@ -102,7 +103,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     onClick = (e: React.MouseEvent): void => {
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress);
+            if (this.props.isSelected()) {
+                PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress);
+            }
             // let the DocumentView stopPropagation of this event when it selects this document
         } else {  // why do we get a click event when the cursor have moved a big distance?
             // let's cut it off here so no one else has to deal with it.
@@ -137,6 +140,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             e.stopPropagation();
         }
         if (e.key === "c" || e.key === "r" || e.key === "e") {
+            console.log("DO MARQUEE");
             e.stopPropagation();
             let bounds = this.Bounds;
             let selected = this.marqueeSelect().map(d => {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index aaedd5b1c..3d6e0c4d8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -172,6 +172,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointerup", this.onPointerUp);
             e.preventDefault();
+            e.stopPropagation();
         }
     }
     onPointerMove = (e: PointerEvent): void => {
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index f4f37250f..727d3c0b2 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -10,7 +10,7 @@
   outline: none !important;
 }
 
-.formattedTextBox-cont-scroll, .formattedTextBox-cont-hidden {
+.formattedTextBox-cont-scroll, .formattedTextBox-cont-hidden  {
   background: $light-color-secondary;
   padding: 0;
   border-width: 0px;
@@ -24,10 +24,19 @@
   height: 100%;
   pointer-events: all;
 }
+
 .formattedTextBox-cont-hidden {
     overflow: hidden;
     pointer-events: none;
 }
+.formattedTextBox-inner-rounded {
+    height: calc(100% - 40px);
+    width: calc(100% - 40px);
+    position: absolute;
+    overflow: scroll;
+    top: 20;
+    left: 20;
+}
 
 .menuicon {
   display: inline-block;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 8fb86f320..02d98de6b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -270,11 +270,12 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         };
     }
     render() {
-        let style = this.props.isOverlay ? "scroll" : "hidden";
+        let style = this.props.isOverlay ? "-scroll" : "-hidden";
+        let rounded = this.props.Document.GetNumber(KeyStore.BorderRounding, 0) < 0 ? "-rounded" : "";
         let color = this.props.Document.GetText(KeyStore.BackgroundColor, "");
-        let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
+        let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
         return (
-            <div className={`formattedTextBox-cont-${style}`}
+            <div className={`formattedTextBox-cont${style}`}
                 style={{
                     pointerEvents: interactive ? "all" : "none",
                     background: color,
@@ -288,7 +289,9 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 onContextMenu={this.specificContextMenu}
                 // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
-                ref={this._ref} />
+            >
+                <div className={`formattedTextBox-inner${rounded}`} ref={this._ref} />
+            </div>
         );
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 7a820b3b175ea52712927e75ce16e32f0ed3a131 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sat, 27 Apr 2019 19:51:58 -0400
Subject: Compiling

---
 src/client/util/DragManager.ts                     |  8 +++----
 src/client/views/MainOverlayTextBox.tsx            | 17 +++++++-------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        | 17 ++++++--------
 src/fields/RichTextField.ts                        | 26 ----------------------
 src/new_fields/RichTextField.ts                    | 12 ++++++++++
 src/new_fields/Types.ts                            | 14 ++++++------
 7 files changed, 39 insertions(+), 57 deletions(-)
 delete mode 100644 src/fields/RichTextField.ts
 create mode 100644 src/new_fields/RichTextField.ts

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0174b2f54..e65f2b9ed 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -158,13 +158,13 @@ export namespace DragManager {
     }
 
     export class LinkDragData {
-        constructor(linkSourceDoc: Document, blacklist: Document[] = []) {
+        constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) {
             this.linkSourceDocument = linkSourceDoc;
             this.blacklist = blacklist;
         }
-        droppedDocuments: Document[] = [];
-        linkSourceDocument: Document;
-        blacklist: Document[];
+        droppedDocuments: Doc[] = [];
+        linkSourceDocument: Doc;
+        blacklist: Doc[];
         [id: string]: any;
     }
 
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index be8d67925..a7ced36bf 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -2,16 +2,15 @@ import { action, observable, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
-import { Document } from '../../fields/Document';
-import { Key } from '../../fields/Key';
-import { KeyStore } from '../../fields/KeyStore';
-import { emptyDocFunction, emptyFunction, returnTrue, returnZero } from '../../Utils';
+import { emptyFunction, returnTrue, returnZero } from '../../Utils';
 import '../northstar/model/ModelExtensions';
 import '../northstar/utils/Extensions';
 import { DragManager } from '../util/DragManager';
 import { Transform } from '../util/Transform';
 import "./MainOverlayTextBox.scss";
 import { FormattedTextBox } from './nodes/FormattedTextBox';
+import { Doc } from '../../new_fields/Doc';
+import { NumCast } from '../../new_fields/Types';
 
 interface MainOverlayTextBoxProps {
 }
@@ -19,11 +18,11 @@ interface MainOverlayTextBoxProps {
 @observer
 export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> {
     public static Instance: MainOverlayTextBox;
-    @observable public TextDoc?: Document = undefined;
+    @observable public TextDoc?: Doc = undefined;
     public TextScroll: number = 0;
     @observable _textRect: any;
     @observable _textXf: () => Transform = () => Transform.Identity();
-    private _textFieldKey: Key = KeyStore.Data;
+    private _textFieldKey: string = "data";
     private _textColor: string | null = null;
     private _textTargetDiv: HTMLDivElement | undefined;
     private _textProxyDiv: React.RefObject<HTMLDivElement>;
@@ -35,7 +34,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     }
 
     @action
-    SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: () => Transform) {
+    SetTextDoc(textDoc?: Doc, textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform) {
         if (this._textTargetDiv) {
             this._textTargetDiv.style.color = this._textColor;
         }
@@ -94,10 +93,10 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
             let s = 1 / this._textXf().Scale;
             return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${pt[0]}px, ${pt[1]}px) scale(${s},${s})`, width: "auto", height: "auto" }} >
                 <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
-                    style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}>
+                    style={{ width: `${NumCast(this.TextDoc.width)}px`, height: `${NumCast(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} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
+                        ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} />
                 </div>
             </ div>;
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 80900c450..047fbad18 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -97,7 +97,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     if (!NumCast(d.height)) {
                         let nw = NumCast(d.nativeWidth);
                         let nh = NumCast(d.nativeHeight);
-                        d.height = nw && nh ? nh / nw * d.Width() : 300;
+                        d.height = nw && nh ? nh / nw * NumCast(d.Width) : 300;
                     }
                     this.bringToFront(d);
                 });
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index c65b1ac89..7a85c9dd3 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -20,7 +20,8 @@ import { createSchema, makeInterface } from "../../../new_fields/Schema";
 import { Opt, Doc } from "../../../new_fields/Doc";
 import { observer } from "mobx-react";
 import { InkingControl } from "../InkingControl";
-import { StrCast } from "../../../new_fields/Types";
+import { StrCast, Cast } from "../../../new_fields/Types";
+import { RichTextField } from "../../../new_fields/RichTextField";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -77,11 +78,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
             this._applyingChange = true;
-            this.props.Document.SetDataOnPrototype(
-                this.props.fieldKey,
-                JSON.stringify(state.toJSON()),
-                RichTextField
-            );
+            Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new RichTextField(JSON.stringify(state.toJSON())));
             Doc.SetOnPrototype(this.props.Document, "documentText", state.doc.textBetween(0, state.doc.content.size, "\n\n"));
             this._applyingChange = false;
             // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
@@ -127,8 +124,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
         this._reactionDisposer = reaction(
             () => {
-                const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined;
-                return field && field !== FieldWaiting ? field.Data : undefined;
+                const field = this.props.Document ? Cast(this.props.Document[this.props.fieldKey], RichTextField) : undefined;
+                return field ? field.Data : undefined;
             },
             field => field && this._editorView && !this._applyingChange &&
                 this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
@@ -136,8 +133,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         this.setupEditor(config, this.props.Document);
     }
 
-    private setupEditor(config: any, doc?: Document) {
-        let field = doc ? doc.GetT(this.props.fieldKey, RichTextField) : undefined;
+    private setupEditor(config: any, doc?: Doc) {
+        let field = doc ? Cast(doc[this.props.fieldKey], RichTextField) : undefined;
         if (this._ref.current) {
             this._editorView = new EditorView(this._ref.current, {
                 state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
deleted file mode 100644
index f53f48ca6..000000000
--- a/src/fields/RichTextField.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { BasicField } from "./BasicField";
-import { Types } from "../server/Message";
-import { FieldId } from "./Field";
-
-export class RichTextField extends BasicField<string> {
-    constructor(data: string = "", id?: FieldId, save: boolean = true) {
-        super(data, save, id);
-    }
-
-    ToScriptString(): string {
-        return `new RichTextField(${this.Data})`;
-    }
-
-    Copy() {
-        return new RichTextField(this.Data);
-    }
-
-    ToJson() {
-        return {
-            type: Types.RichText,
-            data: this.Data,
-            id: this.Id
-        };
-    }
-
-}
\ No newline at end of file
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
new file mode 100644
index 000000000..156e4efd9
--- /dev/null
+++ b/src/new_fields/RichTextField.ts
@@ -0,0 +1,12 @@
+import { ObjectField } from "./Doc";
+import { serializable } from "serializr";
+
+export class RichTextField extends ObjectField {
+    @serializable(true)
+    readonly Data: string;
+
+    constructor(data: string) {
+        super();
+        this.Data = data;
+    }
+}
\ No newline at end of file
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 4808d6e46..7fa18673f 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -38,10 +38,10 @@ export interface Interface {
 }
 
 export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal: WithoutList<ToType<T>>): WithoutList<ToType<T>>;
-export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal?: ToType<T>): FieldResult<ToType<T>> | undefined {
+export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal: WithoutList<ToType<T>> | null): WithoutList<ToType<T>>;
+export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
     if (field instanceof Promise) {
-        return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal;
+        return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal;
     }
     if (field !== undefined && !(field instanceof Promise)) {
         if (typeof ctor === "string") {
@@ -56,18 +56,18 @@ export function Cast<T extends ToConstructor<Field> | ListSpec<Field>>(field: Fi
             return field as ToType<T>;
         }
     }
-    return defaultVal;
+    return defaultVal === null ? undefined : defaultVal;
 }
 
-export function NumCast(field: FieldResult, defaultVal: Opt<number> = 0) {
+export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
     return Cast(field, "number", defaultVal);
 }
 
-export function StrCast(field: FieldResult, defaultVal: Opt<string> = "") {
+export function StrCast(field: FieldResult, defaultVal: string | null = "") {
     return Cast(field, "string", defaultVal);
 }
 
-export function BoolCast(field: FieldResult, defaultVal: Opt<boolean> = undefined) {
+export function BoolCast(field: FieldResult, defaultVal: boolean | null = null) {
     return Cast(field, "boolean", defaultVal);
 }
 
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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/collections/collectionFreeForm/CollectionFreeFormView.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 a2751d16babb38cde2b86b1cb8fc5d74c15762d7 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 28 Apr 2019 00:23:18 -0400
Subject: Decent amount of stuff is working

---
 src/client/views/Main.tsx                                             | 2 +-
 src/client/views/collections/CollectionBaseView.tsx                   | 2 +-
 src/client/views/collections/CollectionPDFView.tsx                    | 3 ++-
 src/client/views/collections/CollectionVideoView.tsx                  | 3 ++-
 src/client/views/collections/CollectionView.tsx                       | 3 ++-
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 2 +-
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx             | 1 +
 src/client/views/nodes/DocumentView.tsx                               | 4 ++--
 src/client/views/nodes/FormattedTextBox.tsx                           | 3 ++-
 src/client/views/nodes/PDFBox.tsx                                     | 2 +-
 src/new_fields/RichTextField.ts                                       | 2 ++
 src/new_fields/Types.ts                                               | 2 +-
 12 files changed, 18 insertions(+), 11 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 4a68d1c68..98c5a5306 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -165,7 +165,7 @@ export class Main extends React.Component {
     @action
     openWorkspace = async (doc: Doc, fromHistory = false) => {
         this.mainContainer = doc;
-        fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc.Id);
+        fromHistory || window.history.pushState(null, StrCast(doc.title), "/doc/" + doc[Id]);
         const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc);
         // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
         setTimeout(async () => {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index b2fba1415..ed761d3f3 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -95,7 +95,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
             //TODO This won't create the field if it doesn't already exist
             const value = Cast(props.Document[props.fieldKey], listSpec(Doc));
             if (value !== undefined) {
-                if (allowDuplicates || !value.some(v => v.Id === doc.Id)) {
+                if (allowDuplicates || !value.some(v => v[Id] === doc[Id])) {
                     value.push(doc);
                 }
             } else {
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 99438b4e8..5a1af354a 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -8,6 +8,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView";
 import { emptyFunction } from "../../../Utils";
 import { NumCast } from "../../../new_fields/Types";
+import { Id } from "../../../new_fields/RefField";
 
 
 @observer
@@ -33,7 +34,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+        if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction });
         }
     }
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index d45be228a..7232ecea2 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -8,6 +8,7 @@ import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormV
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { emptyFunction } from "../../../Utils";
 import { NumCast } from "../../../new_fields/Types";
+import { Id } from "../../../new_fields/RefField";
 
 
 @observer
@@ -107,7 +108,7 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
-        if (!e.isPropagationStopped() && this.props.Document.Id !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+        if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "VideoOptions", event: emptyFunction });
         }
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index b72065bca..c2049a09a 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -10,6 +10,7 @@ import { CurrentUserUtils } from '../../../server/authentication/models/current_
 import { observer } from 'mobx-react';
 import { undoBatch } from '../../util/UndoManager';
 import { trace } from 'mobx';
+import { Id } from '../../../new_fields/RefField';
 
 @observer
 export class CollectionView extends React.Component<FieldViewProps> {
@@ -31,7 +32,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
     get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; } // bcz: ? Why do we need to compare Id's?
 
     onContextMenu = (e: React.MouseEvent): void => {
-        if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document.Id !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+        if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform) });
             ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema) });
             ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree) });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dfacca204..dcded7648 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -257,7 +257,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             moveDocument: this.props.moveDocument,
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
-            selectOnLoad: document.Id === this._selectOnLoaded,
+            selectOnLoad: document[Id] === this._selectOnLoaded,
             PanelWidth: () => Cast(document.width, "number", 0),//TODO Types These are inline functions
             PanelHeight: () => Cast(document.height, "number", 0),
             ContentScaling: returnOne,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 376af0b36..8766eb7ea 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -20,6 +20,7 @@ const schema = createSchema({
     zIndex: "number"
 });
 
+//TODO Types: The import order is wrong, so positionSchema is undefined
 type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>;
 const FreeformDocument = makeInterface(schema, positionSchema);
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c304b6a35..3814eeb9c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -139,7 +139,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     onClick = (e: React.MouseEvent): void => {
-        if (CurrentUserUtils.MainDocId !== this.props.Document.Id &&
+        if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
                 Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             SelectionManager.SelectDoc(this, e.ctrlKey);
@@ -247,7 +247,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked });
         ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) });
         ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) });
-        ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document.Id)) });
+        ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])) });
         ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]) });
         //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
         ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked });
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 96512718f..c4c720ca9 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -22,6 +22,7 @@ import { observer } from "mobx-react";
 import { InkingControl } from "../InkingControl";
 import { StrCast, Cast } from "../../../new_fields/Types";
 import { RichTextField } from "../../../new_fields/RichTextField";
+import { Id } from "../../../new_fields/RefField";
 const { buildMenuItems } = require("prosemirror-example-setup");
 const { menuBar } = require("prosemirror-menu");
 
@@ -108,7 +109,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         };
 
         if (this.props.isOverlay) {
-            this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc.Id,
+            this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc[Id],
                 () => {
                     if (this._editorView) {
                         this._editorView.destroy();
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 7fbfed1c0..9f0849492 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -447,7 +447,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField);
         let xf = FieldValue(this.Document.nativeHeight, 0) / renderHeight;
         return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
-            <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`} renderMode={this._renderAsSvg ? "svg" : ""}>
+            <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl!.url.href}`} renderMode={this._renderAsSvg ? "svg" : ""}>
                 <Measure onResize={this.setScaling}>
                     {({ measureRef }) =>
                         <div className="pdfBox-page" ref={measureRef}>
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index 0fa3cf73c..f2033d5a7 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -1,6 +1,8 @@
 import { ObjectField } from "./ObjectField";
 import { serializable } from "serializr";
+import { Deserializable } from "../client/util/SerializationHelper";
 
+@Deserializable("RichTextField")
 export class RichTextField extends ObjectField {
     @serializable(true)
     readonly Data: string;
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 3f8eabd45..e179c2602 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -76,7 +76,7 @@ type WithoutList<T extends Field> = T extends List<infer R> ? R[] : T;
 export function FieldValue<T extends Field, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
 export function FieldValue<T extends Field>(field: FieldResult<T>): Opt<T>;
 export function FieldValue<T extends Field>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
-    return field instanceof Promise ? defaultValue : field;
+    return (field instanceof Promise || field === undefined) ? defaultValue : field;
 }
 
 export interface PromiseLike<T> {
-- 
cgit v1.2.3-70-g09d2


From 877b104a61d2ab072e3b6a006168ec03e2c46365 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 30 Apr 2019 00:11:27 -0400
Subject: Mostly fixed lists

---
 src/client/util/UndoManager.ts                     |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   1 +
 src/debug/Test.tsx                                 |   8 +-
 src/new_fields/Doc.ts                              |  10 +-
 src/new_fields/List.ts                             | 192 +++++++++++++++++----
 src/new_fields/util.ts                             |   8 +
 6 files changed, 179 insertions(+), 44 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index f7c3e5a7b..0b5280c4a 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -141,10 +141,10 @@ export namespace UndoManager {
     });
 
     //TODO Make this return the return value
-    export function RunInBatch(fn: () => void, batchName: string) {
+    export function RunInBatch<T>(fn: () => T, batchName: string) {
         let batch = StartBatch(batchName);
         try {
-            runInAction(fn);
+            return runInAction(fn);
         } finally {
             batch.end();
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dcded7648..d796bd8d5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -272,6 +272,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     get views() {
         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);
             if (page === curPage || page === -1) {
                 let minim = Cast(doc.isMinimized, "boolean");
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 7415d4b28..04ef00722 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -61,14 +61,12 @@ class Test extends React.Component {
         assert(test2.testDoc === undefined);
         test2.url = 35;
         assert(test2.url === 35);
-        const l = new List<number>();
+        const l = new List<Doc>();
         //TODO push, and other array functions don't go through the proxy
-        l.push(1);
+        l.push(doc2);
         //TODO currently length, and any other string fields will get serialized
-        l.length = 3;
-        l[2] = 5;
+        doc.list = l;
         console.log(l.slice());
-        console.log(SerializationHelper.Serialize(l));
     }
 
     render() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index d15b6309d..6ddb0df89 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -3,7 +3,7 @@ import { serializable, primitive, map, alias, list } from "serializr";
 import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper";
 import { Utils } from "../Utils";
 import { DocServer } from "../client/DocServer";
-import { setter, getter, getField, updateFunction } from "./util";
+import { setter, getter, getField, updateFunction, deleteProperty } from "./util";
 import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types";
 import { UndoManager, undoBatch } from "../client/util/UndoManager";
 import { listSpec } from "./Schema";
@@ -34,7 +34,7 @@ export class Doc extends RefField {
             set: setter,
             get: getter,
             ownKeys: target => Object.keys(target.__fields),
-            deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); },
+            deleteProperty: deleteProperty,
             defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
         });
         if (!id || forceSave) {
@@ -151,8 +151,8 @@ export namespace Doc {
     }
 
     export function MakeLink(source: Doc, target: Doc): Doc {
-        let linkDoc = new Doc;
-        UndoManager.RunInBatch(() => {
+        return UndoManager.RunInBatch(() => {
+            let linkDoc = new Doc;
             linkDoc.title = "New Link";
             linkDoc.linkDescription = "";
             linkDoc.linkTags = "Default";
@@ -171,8 +171,8 @@ export namespace Doc {
                 source.linkedToDocs = linkedTo = new List<Doc>();
             }
             linkedTo.push(linkDoc);
+            return linkDoc;
         }, "make link");
-        return linkDoc;
     }
 
     export function MakeDelegate(doc: Doc): Doc;
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index e4a80f7a1..ec1bf44a9 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -1,21 +1,173 @@
 import { Deserializable, autoObject } from "../client/util/SerializationHelper";
 import { Field, Update, Self } from "./Doc";
-import { setter, getter } from "./util";
+import { setter, getter, deleteProperty } from "./util";
 import { serializable, alias, list } from "serializr";
 import { observable, observe, IArrayChange, IArraySplice, IObservableArray, Lambda, reaction } from "mobx";
 import { ObjectField, OnUpdate } from "./ObjectField";
+import { RefField } from "./RefField";
+import { ProxyField } from "./Proxy";
 
 const listHandlers: any = {
-    push(...items: any[]) {
-        // console.log("push");
-        // console.log(...items);
-        return this[Self].__fields.push(...items);
+    /// Mutator methods
+    copyWithin() {
+        throw new Error("copyWithin not supported yet");
+    },
+    fill(value: any, start?: number, end?: number) {
+        if (value instanceof RefField) {
+            throw new Error("fill with RefFields not supported yet");
+        }
+        const res = this[Self].__fields.fill(value, start, end);
+        this[Update]();
+        return res;
     },
     pop(): any {
-        return this[Self].__fields.pop();
+        const field = toRealField(this[Self].__fields.pop());
+        this[Update]();
+        return field;
+    },
+    push(...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]();
+        return res;
+    },
+    shift() {
+        const res = toRealField(this[Self].__fields.shift());
+        this[Update]();
+        return res;
+    },
+    sort(cmpFunc: any) {
+        const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
+        this[Update]();
+        return res;
+    },
+    splice(start: number, deleteCount: number, ...items: any[]) {
+        items = items.map(toObjectField);
+        const res = this[Self].__fields.splice(start, deleteCount, ...items);
+        this[Update]();
+        return res.map(toRealField);
+    },
+    unshift(...items: any[]) {
+        items = items.map(toObjectField);
+        const res = this[Self].__fields.unshift(...items);
+        this[Update]();
+        return res;
+
+    },
+    /// Accessor methods
+    concat(...items: any[]) {
+        return this[Self].__fields.map(toRealField).concat(...items);
+    },
+    includes(valueToFind: any, fromIndex: number) {
+        const fields = this[Self].__fields;
+        if (valueToFind instanceof RefField) {
+            return fields.map(toRealField).includes(valueToFind, fromIndex);
+        } else {
+            return fields.includes(valueToFind, fromIndex);
+        }
+    },
+    indexOf(valueToFind: any, fromIndex: number) {
+        const fields = this[Self].__fields;
+        if (valueToFind instanceof RefField) {
+            return fields.map(toRealField).indexOf(valueToFind, fromIndex);
+        } else {
+            return fields.indexOf(valueToFind, fromIndex);
+        }
+    },
+    join(separator: any) {
+        return this[Self].__fields.map(toRealField).join(separator);
+    },
+    lastIndexOf(valueToFind: any, fromIndex: number) {
+        const fields = this[Self].__fields;
+        if (valueToFind instanceof RefField) {
+            return fields.map(toRealField).lastIndexOf(valueToFind, fromIndex);
+        } else {
+            return fields.lastIndexOf(valueToFind, fromIndex);
+        }
+    },
+    slice(begin: number, end: number) {
+        return this[Self].__fields.slice(begin, end).map(toRealField);
+    },
+
+    /// Iteration methods
+    entries() {
+        return this[Self].__fields.map(toRealField).entries();
+    },
+    every(callback: any, thisArg: any) {
+        return this[Self].__fields.map(toRealField).every(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    filter(callback: any, thisArg: any) {
+        return this[Self].__fields.map(toRealField).filter(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    find(callback: any, thisArg: any) {
+        return this[Self].__fields.map(toRealField).find(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    findIndex(callback: any, thisArg: any) {
+        return this[Self].__fields.map(toRealField).findIndex(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    forEach(callback: any, thisArg: any) {
+        return this[Self].__fields.map(toRealField).forEach(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    map(callback: any, thisArg: any) {
+        return this[Self].__fields.map(toRealField).map(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    reduce(callback: any, initialValue: any) {
+        return this[Self].__fields.map(toRealField).reduce(callback, initialValue);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+    },
+    reduceRight(callback: any, initialValue: any) {
+        return this[Self].__fields.map(toRealField).reduceRight(callback, initialValue);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
+    },
+    some(callback: any, thisArg: any) {
+        return this[Self].__fields.map(toRealField).some(callback, thisArg);
+        // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
+        // If we don't want to support the array parameter, we should use this version instead
+        // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
+    },
+    values() {
+        return this[Self].__fields.map(toRealField).values();
+    },
+    [Symbol.iterator]() {
+        return this[Self].__fields.map(toRealField).values();
     }
 };
 
+function toObjectField(field: Field) {
+    return field instanceof RefField ? new ProxyField(field) : field;
+}
+
+function toRealField(field: Field) {
+    return field instanceof ProxyField ? field.value() : field;
+}
+
 function listGetter(target: any, prop: string | number | symbol, receiver: any): any {
     if (listHandlers.hasOwnProperty(prop)) {
         return listHandlers[prop];
@@ -38,36 +190,15 @@ interface ListIndexUpdate<T> {
 
 type ListUpdate<T> = ListSpliceUpdate<T> | ListIndexUpdate<T>;
 
-const ObserveDisposer = Symbol("Observe Disposer");
-
-function listObserver<T extends Field>(this: ListImpl<T>, change: IArrayChange<T> | IArraySplice<T>) {
-    if (change.type === "splice") {
-        this[Update]({
-            index: change.index,
-            removedCount: change.removedCount,
-            added: change.added,
-            type: change.type
-        });
-    } else {
-        //This should already be handled by the getter for the Proxy
-        // this[Update]({
-        //     index: change.index,
-        //     newValue: change.newValue,
-        //     type: change.type
-        // });
-    }
-}
-
 @Deserializable("list")
 class ListImpl<T extends Field> extends ObjectField {
     constructor(fields: T[] = []) {
         super();
         this.___fields = fields;
-        this[ObserveDisposer] = observe(this.__fields as IObservableArray<T>, listObserver.bind(this));
         const list = new Proxy<this>(this, {
             set: setter,
-            get: getter,
-            deleteProperty: () => { throw new Error("Currently properties can't be deleted from documents, assign to undefined instead"); },
+            get: listGetter,
+            deleteProperty: deleteProperty,
             defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
         });
         return list;
@@ -82,8 +213,6 @@ class ListImpl<T extends Field> extends ObjectField {
 
     private set __fields(value) {
         this.___fields = value;
-        this[ObserveDisposer]();
-        this[ObserveDisposer] = observe(this.__fields as IObservableArray<T>, listObserver.bind(this));
     }
 
     // @serializable(alias("fields", list(autoObject())))
@@ -97,7 +226,6 @@ class ListImpl<T extends Field> extends ObjectField {
         update && update();
     }
 
-    private [ObserveDisposer]: Lambda;
     private [Self] = this;
 }
 export type List<T extends Field> = ListImpl<T> & T[];
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 511820115..128817ab8 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -78,6 +78,14 @@ export function getField(target: any, prop: string | number, ignoreProto: boolea
     return field;
 }
 
+export function deleteProperty(target: any, prop: string | number | symbol) {
+    if (typeof prop === "symbol") {
+        delete target[prop];
+        return true;
+    }
+    throw new Error("Currently properties can't be deleted from documents, assign to undefined instead");
+}
+
 export function updateFunction(target: any, prop: any, value: any) {
     return (diff?: any) => {
         if (!diff) diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } };
-- 
cgit v1.2.3-70-g09d2


From e013b7b146f91b0ffbc26e3770f5f90f417da60b Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 30 Apr 2019 12:09:54 -0400
Subject: fixed inking

---
 src/client/views/InkingCanvas.tsx                      | 18 +++++++++++-------
 src/client/views/InkingStroke.scss                     |  3 +++
 src/client/views/InkingStroke.tsx                      |  8 ++++++--
 .../collectionFreeForm/CollectionFreeFormView.tsx      | 17 ++++++++++-------
 src/new_fields/Doc.ts                                  |  6 +++++-
 5 files changed, 35 insertions(+), 17 deletions(-)
 create mode 100644 src/client/views/InkingStroke.scss

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx
index 1e26893c5..1c0d13545 100644
--- a/src/client/views/InkingCanvas.tsx
+++ b/src/client/views/InkingCanvas.tsx
@@ -139,21 +139,25 @@ export class InkingCanvas extends React.Component<InkCanvasProps> {
         let curPage = NumCast(this.props.Document.curPage, -1);
         let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => {
             if (strokeData.page === -1 || strokeData.page === curPage) {
-                paths.push(<InkingStroke key={id} id={id} line={strokeData.pathData}
+                paths.push(<InkingStroke key={id} id={id}
+                    line={strokeData.pathData}
+                    count={strokeData.pathData.length}
                     offsetX={this.maxCanvasDim - this.inkMidX}
                     offsetY={this.maxCanvasDim - this.inkMidY}
-                    color={strokeData.color} width={strokeData.width}
-                    tool={strokeData.tool} deleteCallback={this.removeLine} />);
+                    color={strokeData.color}
+                    width={strokeData.width}
+                    tool={strokeData.tool}
+                    deleteCallback={this.removeLine} />);
             }
             return paths;
         }, [] as JSX.Element[]);
-        return [<svg className={`inkingCanvas-paths-markers`} key="Markers"
+        return [<svg className={`inkingCanvas-paths-ink`} key="Pens"
             style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} >
-            {paths.filter(path => path.props.tool === InkTool.Highlighter)}
+            {paths.filter(path => path.props.tool !== InkTool.Highlighter)}
         </svg>,
-        <svg className={`inkingCanvas-paths-ink`} key="Pens"
+        <svg className={`inkingCanvas-paths-markers`} key="Markers"
             style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}>
-            {paths.filter(path => path.props.tool !== InkTool.Highlighter)}
+            {paths.filter(path => path.props.tool === InkTool.Highlighter)}
         </svg>];
     }
 
diff --git a/src/client/views/InkingStroke.scss b/src/client/views/InkingStroke.scss
new file mode 100644
index 000000000..cdbfdcff3
--- /dev/null
+++ b/src/client/views/InkingStroke.scss
@@ -0,0 +1,3 @@
+.inkingStroke-marker {
+    mix-blend-mode: multiply
+}
\ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 616299146..37b1f5899 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,14 +1,16 @@
 import { observer } from "mobx-react";
-import { observable } from "mobx";
+import { observable, trace } from "mobx";
 import { InkingControl } from "./InkingControl";
 import React = require("react");
 import { InkTool } from "../../new_fields/InkField";
+import "./InkingStroke.scss";
 
 
 interface StrokeProps {
     offsetX: number;
     offsetY: number;
     id: string;
+    count: number;
     line: Array<{ x: number, y: number }>;
     color: string;
     width: string;
@@ -48,10 +50,12 @@ export class InkingStroke extends React.Component<StrokeProps> {
     render() {
         let pathStyle = this.createStyle();
         let pathData = this.parseData(this.props.line);
+        let pathlength = this.props.count; // bcz: this is needed to force reactions to the line data changes
+        let marker = this.props.tool === InkTool.Highlighter ? "-marker" : "";
 
         let pointerEvents: any = InkingControl.Instance.selectedTool === InkTool.Eraser ? "all" : "none";
         return (
-            <path d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round"
+            <path className={`inkingStroke${marker}`} d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round"
                 onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} />
         );
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d796bd8d5..2689f48cd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,4 +1,4 @@
-import { action, computed } from "mobx";
+import { action, computed, trace } from "mobx";
 import { observer } from "mobx-react";
 import { emptyFunction, returnFalse, returnOne } from "../../../../Utils";
 import { DocumentManager } from "../../../util/DocumentManager";
@@ -19,7 +19,7 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc";
 import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
 import { pageSchema } from "../../nodes/ImageBox";
 import { List } from "../../../../new_fields/List";
@@ -243,11 +243,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     focusDocument = (doc: Doc) => {
         this.setPan(
-            Cast(doc.x, "number", 0) + Cast(doc.width, "number", 0) / 2,
-            Cast(doc.y, "number", 0) + Cast(doc.height, "number", 0) / 2);
+            NumCast(doc.x) + NumCast(doc.width) / 2,
+            NumCast(doc.y) + NumCast(doc.height) / 2);
         this.props.focus(this.props.Document);
     }
 
+
     getDocumentViewProps(document: Doc): DocumentViewProps {
         return {
             Document: document,
@@ -258,8 +259,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             ScreenToLocalTransform: this.getTransform,
             isTopMost: false,
             selectOnLoad: document[Id] === this._selectOnLoaded,
-            PanelWidth: () => Cast(document.width, "number", 0),//TODO Types These are inline functions
-            PanelHeight: () => Cast(document.height, "number", 0),
+            PanelWidth: document[WidthSym],
+            PanelHeight: document[HeightSym],
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
@@ -268,8 +269,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         };
     }
 
-    @computed
+    @computed.struct
     get views() {
+        trace();
         let curPage = FieldValue(this.Document.curPage, -1);
         let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
             if (!FieldValue(doc)) return prev;
@@ -295,6 +297,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.getDocumentViewProps(this.props.Document)} />];
     render() {
+        trace();
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 6ddb0df89..0e5105761 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -4,7 +4,7 @@ import { autoObject, SerializationHelper, Deserializable } from "../client/util/
 import { Utils } from "../Utils";
 import { DocServer } from "../client/DocServer";
 import { setter, getter, getField, updateFunction, deleteProperty } from "./util";
-import { Cast, ToConstructor, PromiseValue, FieldValue } from "./Types";
+import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types";
 import { UndoManager, undoBatch } from "../client/util/UndoManager";
 import { listSpec } from "./Schema";
 import { List } from "./List";
@@ -25,6 +25,8 @@ export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract
 
 export const Update = Symbol("Update");
 export const Self = Symbol("Self");
+export const WidthSym = Symbol("Width");
+export const HeightSym = Symbol("Height");
 
 @Deserializable("doc").withFields(["id"])
 export class Doc extends RefField {
@@ -70,6 +72,8 @@ export class Doc extends RefField {
     }
 
     private [Self] = this;
+    public [WidthSym] = () => { return NumCast(this.__fields.width); }  // bcz: is this the right way to access width/height?   it didn't work with : this.width
+    public [HeightSym] = () => { return NumCast(this.__fields.height); }
 }
 
 export namespace Doc {
-- 
cgit v1.2.3-70-g09d2


From 37062ef8b33efebd9fafcd2c076d5cb49f5a288f Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 30 Apr 2019 13:43:15 -0400
Subject: from commit

---
 src/client/documents/Documents.ts                            | 12 ++++--------
 src/client/views/DocumentDecorations.tsx                     |  3 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  8 ++------
 3 files changed, 7 insertions(+), 16 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index aa5277910..ff21dd5b0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -32,11 +32,7 @@ import { IconField } from "../../new_fields/IconField";
 import { listSpec } from "../../new_fields/Schema";
 import { DocServer } from "../DocServer";
 import { StrokeData, InkField } from "../../new_fields/InkField";
-import { KeyStore } from "../../fields/KeyStore";
 
-// export class stringArray implements List<any>  {
-//     public Values: string[] = [];
-// }
 export interface DocumentOptions {
     x?: number;
     y?: number;
@@ -60,7 +56,7 @@ export interface DocumentOptions {
     curPage?: number;
     documentText?: string;
     borderRounding?: number;
-    //schemaColumns?: stringArray;
+    schemaColumns?: List<string>;
     // [key: string]: Opt<Field>;
 }
 const delegateKeys = ["x", "y", "width", "height", "panX", "panY"];
@@ -243,13 +239,13 @@ export namespace Docs {
         if (!makePrototype) {
             return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, new List(documents));
         }
-        return CreateInstance(collProto, new List(documents), { /*schemaColumns: [KeyStore.SchemaColumns],*/...options, viewType: CollectionViewType.Freeform });
+        return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["schemaColumns"]), ...options, viewType: CollectionViewType.Freeform });
     }
     export function SchemaDocument(documents: Array<Doc>, options: DocumentOptions) {
-        return CreateInstance(collProto, new List(documents), { /*schemaColumns: [KeyStore.SchemaColumns], */...options, viewType: CollectionViewType.Schema });
+        return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["schemaColumns"]), ...options, viewType: CollectionViewType.Schema });
     }
     export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) {
-        return CreateInstance(collProto, new List(documents), { /*schemaColumns: [KeyStore.SchemaColumns], */...options, viewType: CollectionViewType.Tree });
+        return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["schemaColumns"]), ...options, viewType: CollectionViewType.Tree });
     }
     export function DockDocument(config: string, options: DocumentOptions) {
         return CreateInstance(collProto, config, { ...options, viewType: CollectionViewType.Docking });
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 556117c54..8632286ab 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -18,7 +18,6 @@ import { Doc, FieldResult } from "../../new_fields/Doc";
 import { listSpec } from "../../new_fields/Schema";
 import { Docs } from "../documents/Documents";
 import { List } from "../../new_fields/List";
-import { KeyStore } from "../../fields/KeyStore";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -40,7 +39,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @observable private _minimizedY = 0;
     @observable private _title: string = "";
     @observable private _edtingTitle = false;
-    @observable private _fieldKey = KeyStore.Title;
+    @observable private _fieldKey = "title";
     @observable private _hidden = false;
     @observable private _opacity = 1;
     @observable private _iconifying = false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f99800e21..8d10666b7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -22,9 +22,7 @@ import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Sc
 import { Doc, WidthSym, HeightSym } 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";
-import { KeyStore } from "../../../../fields/KeyStore";
 
 export const panZoomSchema = createSchema({
     panX: "number",
@@ -272,7 +270,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     @computed.struct
     get views() {
-        trace();
         let curPage = FieldValue(this.Document.curPage, -1);
         let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
             if (!FieldValue(doc)) return prev;
@@ -298,7 +295,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
     render() {
-        trace();
         const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
         return (
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
@@ -333,7 +329,7 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
     @computed get overlayView() {
         let overlayLayout = Cast(this.props.Document.overlayLayout, "string", "");
         return !overlayLayout ? (null) :
-            (<DocumentContentsView {...this.props} layoutKey={KeyStore.OverlayLayout}
+            (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
                 isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
     }
     render() {
@@ -346,7 +342,7 @@ class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps
     @computed get backgroundView() {
         let backgroundLayout = Cast(this.props.Document.backgroundLayout, "string", "");
         return !backgroundLayout ? (null) :
-            (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout}
+            (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
                 isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
     }
     render() {
-- 
cgit v1.2.3-70-g09d2


From 7261d69c9e9dcdbcc413eff062eb01de2032f9ef Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 30 Apr 2019 15:21:20 -0400
Subject: fixed link lines

---
 src/client/views/DocumentDecorations.tsx              |  2 +-
 .../CollectionFreeFormLinksView.tsx                   |  4 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx     | 19 +++++--------------
 .../collections/collectionFreeForm/MarqueeView.tsx    |  4 +---
 .../views/nodes/CollectionFreeFormDocumentView.tsx    |  2 ++
 src/client/views/nodes/DocumentView.tsx               |  9 +++++----
 6 files changed, 16 insertions(+), 24 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 8632286ab..1dc7c8394 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -268,7 +268,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 minDocs.map(minDoc => {
                     minDoc.x = NumCast(minDocs[0].x);
                     minDoc.y = NumCast(minDocs[0].y);
-                    minDoc.linkTags = new List(minDocs);
+                    minDoc.linkTags = new List(minDocs.filter(d => d != minDoc));
                     if (this._iconifying && selectedDocs[0].props.removeDocument) {
                         selectedDocs[0].props.removeDocument(minDoc);
                         (minDoc.maximizedDoc as Doc).minimizedDoc = undefined;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index e5dcf8c29..4a75fccff 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -85,10 +85,10 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
             equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!);
         }
         if (view.props.ContainingCollectionView) {
-            let collid = view.props.ContainingCollectionView.props.Document.Id;
+            let collid = view.props.ContainingCollectionView.props.Document[Id];
             Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).
                 filter(child =>
-                    child.Id === collid).map(view =>
+                    child[Id] === collid).map(view =>
                         DocumentManager.Instance.getDocumentViews(view).map(view =>
                             equalViews.push(view)));
         }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8d10666b7..e54553d2b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -97,7 +97,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     if (!NumCast(d.height)) {
                         let nw = NumCast(d.nativeWidth);
                         let nh = NumCast(d.nativeHeight);
-                        d.height = nw && nh ? nh / nw * NumCast(d.Width) : 300;
+                        d.height = nw && nh ? nh / nw * NumCast(d.width) : 300;
                     }
                     this.bringToFront(d);
                 });
@@ -108,12 +108,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return false;
     }
 
-    @action
-    cleanupInteractions = () => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
-
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         let childSelected = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), [] as Doc[]).filter(doc => doc).reduce((childSelected, doc) => {
@@ -125,7 +119,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                 (e.button === 0 && e.altKey)) && (childSelected || this.props.active()))) ||
             (!CollectionFreeFormView.RIGHT_BTN_DRAG &&
                 ((e.button === 0 && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) && (childSelected || this.props.active())))) {
-            this.cleanupInteractions();
+            document.removeEventListener("pointermove", this.onPointerMove);
+            document.removeEventListener("pointerup", this.onPointerUp);
             document.addEventListener("pointermove", this.onPointerMove);
             document.addEventListener("pointerup", this.onPointerUp);
             this._lastX = e.pageX;
@@ -134,7 +129,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     onPointerUp = (e: PointerEvent): void => {
-        this.cleanupInteractions();
+        document.removeEventListener("pointermove", this.onPointerMove);
+        document.removeEventListener("pointerup", this.onPointerUp);
     }
 
     @action
@@ -300,11 +296,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
                 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} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
                     addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
                     getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1d3850a72..85a12defa 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -109,9 +109,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
     onClick = (e: React.MouseEvent): void => {
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            if (this.props.isSelected()) {
-                PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress);
-            }
+            PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress);
             // let the DocumentView stopPropagation of this event when it selects this document
         } else {  // why do we get a click event when the cursor have moved a big distance?
             // let's cut it off here so no one else has to deal with it.
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index bd084bfe0..c8f0bca91 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -114,6 +114,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         let isMinimized: boolean | undefined;
         let minimizedDocSet = Cast(this.props.Document.linkTags, listSpec(Doc));
         if (!minimizedDocSet) return;
+        let docs = minimizedDocSet.map(d => d);
+        docs.push(this.props.Document);
         minimizedDocSet.map(async minimizedDoc => {
             if (minimizedDoc instanceof Document) {
                 this.props.addDocument && this.props.addDocument(minimizedDoc, false);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ac3dcde42..eab068355 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -308,8 +308,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     isSelected = () => SelectionManager.IsSelected(this);
     select = (ctrlPressed: boolean) => SelectionManager.SelectDoc(this, ctrlPressed);
 
-    @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
-    @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
+    @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
+    @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
     @computed get contents() { return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={"layout"} />); }
 
     render() {
@@ -322,8 +322,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 ref={this._mainCont}
                 style={{
                     borderRadius: "inherit",
-                    background: FieldValue(this.Document.backgroundColor) || "",
-                    width: nativeWidth, height: nativeHeight,
+                    background: this.Document.backgroundColor || "",
+                    width: nativeWidth,
+                    height: nativeHeight,
                     transform: `scale(${scaling}, ${scaling})`
                 }}
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-- 
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/collections/collectionFreeForm/CollectionFreeFormView.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 26b0374d87ef1b14d947d5882f73e67b8607436e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 3 May 2019 10:58:12 -0400
Subject: changes to icon box and decorations.

---
 src/client/views/DocumentDecorations.tsx           | 19 ++++++-------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  3 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  8 ++++--
 src/client/views/nodes/DocumentView.tsx            |  1 +
 src/client/views/nodes/IconBox.scss                | 12 +++++++-
 src/client/views/nodes/IconBox.tsx                 | 32 +++++++++++++++++++---
 6 files changed, 56 insertions(+), 19 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d07adcb71..9771c6580 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -48,14 +48,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     @observable private _fieldKey = "title";
     @observable private _hidden = false;
     @observable private _opacity = 1;
-    @observable private _iconifying = false;
+    @observable private _removeIcon = false;
     @observable public Interacting = false;
 
     constructor(props: Readonly<{}>) {
         super(props);
         DocumentDecorations.Instance = this;
         this.keyinput = React.createRef();
-        reaction(() => SelectionManager.SelectedDocuments().slice(), (docs) => docs.length === 0 && (this._edtingTitle = false));
+        reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this._edtingTitle = false);
     }
 
     @action titleChanged = (event: any) => { this._title = event.target.value; };
@@ -197,6 +197,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (e.button === 0) {
             this._downX = e.pageX;
             this._downY = e.pageY;
+            this._removeIcon = false;
             let selDoc = SelectionManager.SelectedDocuments()[0];
             let selDocPos = selDoc.props.ScreenToLocalTransform().scale(selDoc.props.ContentScaling()).inverse().transformPoint(0, 0);
             this._minimizedX = selDocPos[0] + 12;
@@ -222,18 +223,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             Promise.all(selectedDocs.map(selDoc => this.getIconDoc(selDoc))).then(minDocSet =>
                 this.moveIconDocs(SelectionManager.SelectedDocuments())
             );
-            this._iconifying = snapped;
+            this._removeIcon = snapped;
         }
     }
 
-
     @action createIcon = (docView: DocumentView, layoutString: string): Doc => {
         let doc = docView.props.Document;
         let iconDoc = Docs.IconDocument(layoutString);
         iconDoc.title = "ICON" + StrCast(doc.title);
+        iconDoc.labelField = this._fieldKey;
         iconDoc.isMinimized = false;
-        iconDoc.nativeWidth = Number(MINIMIZED_ICON_SIZE);
-        iconDoc.nativeHeight = Number(MINIMIZED_ICON_SIZE);
         iconDoc.width = Number(MINIMIZED_ICON_SIZE);
         iconDoc.height = Number(MINIMIZED_ICON_SIZE);
         iconDoc.x = NumCast(doc.x);
@@ -275,15 +274,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 minDocs.map(minDoc => {
                     minDoc.x = NumCast(minDocs[0].x);
                     minDoc.y = NumCast(minDocs[0].y);
-                    minDoc.linkTags = new List(minDocs.filter(d => d !== minDoc));
-                    if (this._iconifying && selectedDocs[0].props.removeDocument) {
+                    minDoc.linkedIconTags = new List(minDocs.filter(d => d !== minDoc));
+                    if (this._removeIcon && selectedDocs[0].props.removeDocument) {
                         selectedDocs[0].props.removeDocument(minDoc);
                         (minDoc.maximizedDoc as Doc).minimizedDoc = undefined;
                     }
                 });
                 runInAction(() => this._minimizedX = this._minimizedY = 0);
-                if (!this._iconifying) selectedDocs[0].props.toggleMinimized();
-                this._iconifying = false;
+                selectedDocs[0].props.toggleMinimized();
+                this._removeIcon = false;
             });
         }
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 54b721f7b..17c25c9db 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -225,7 +225,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     onDragOver = (): void => {
     }
 
-    bringToFront(doc: Doc) {
+    bringToFront = (doc: Doc) => {
         const docs = (this.children || []);
         docs.slice().sort((doc1, doc2) => {
             if (doc1 === doc) return 1;
@@ -266,6 +266,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             focus: this.focusDocument,
             parentActive: this.props.active,
             whenActiveChanged: this.props.active,
+            bringToFront: this.bringToFront,
         };
     }
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 09f9c8516..53298fbdd 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -112,10 +112,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     public toggleIcon = async (): Promise<void> => {
         SelectionManager.DeselectAll();
         let isMinimized: boolean | undefined;
-        let minimizedDocSet = Cast(this.props.Document.linkTags, listSpec(Doc));
-        if (!minimizedDocSet) return;
+        let minimizedDocSet = Cast(this.props.Document.linkedIconTags, listSpec(Doc), []);
         let docs = minimizedDocSet.map(d => d);
-        docs.push(this.props.Document);
+        let minimDoc = Cast(this.props.Document.minimizedDoc, Doc);
+        if (minimDoc instanceof Doc) docs.push(minimDoc);
+        else docs.push(this.props.Document);
         docs.map(async minimizedDoc => {
             this.props.addDocument && this.props.addDocument(minimizedDoc, false);
             let maximizedDoc = await Cast(minimizedDoc.maximizedDoc, Doc);
@@ -125,6 +126,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     let maximizedDocMinimizedState = Cast(maximizedDoc.isMinimized, "boolean");
                     isMinimized = (maximizedDocMinimizedState) ? true : false;
                 }
+                if (isMinimized) this.props.bringToFront(maximizedDoc);
                 let minx = NumCast(minimizedDoc.x, undefined);
                 let miny = NumCast(minimizedDoc.y, undefined);
                 let maxx = NumCast(maximizedDoc.x, undefined);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8c245ab3a..892850a69 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -53,6 +53,7 @@ export interface DocumentViewProps {
     parentActive: () => boolean;
     whenActiveChanged: (isActive: boolean) => void;
     toggleMinimized: () => void;
+    bringToFront: (doc: Doc) => void;
 }
 
 const schema = createSchema({
diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss
index ce0ee2e09..85bbdeb59 100644
--- a/src/client/views/nodes/IconBox.scss
+++ b/src/client/views/nodes/IconBox.scss
@@ -4,9 +4,19 @@
     position: absolute;
     left:0;
     top:0;
+    height: 100%;
+    width: max-content;
+    // overflow: hidden;
+    pointer-events: all;
     svg {
-        width: 100% !important;
+        width:  $MINIMIZED_ICON_SIZE !important;
         height: 100%;
         background: white;
     }
+    .iconBox-label {
+        position: inherit;
+        width:max-content;
+        font-size: 14px;
+        margin-top: 3px;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 7a0c49735..08301ae0d 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -2,14 +2,17 @@ import React = require("react");
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed } from "mobx";
+import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { SelectionManager } from "../../util/SelectionManager";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./IconBox.scss";
-import { Cast } from "../../../new_fields/Types";
-import { Doc } from "../../../new_fields/Doc";
+import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
 import { IconField } from "../../../new_fields/IconField";
+import { ContextMenu } from "../ContextMenu";
+import Measure from "react-measure";
+import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
 
 
 library.add(faCaretUp);
@@ -36,10 +39,31 @@ export class IconBox extends React.Component<FieldViewProps> {
         return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
 
+    setLabelField = (e: React.MouseEvent): void => {
+        this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel);
+    }
+
+    specificContextMenu = (e: React.MouseEvent): void => {
+        ContextMenu.Instance.addItem({
+            description: BoolCast(this.props.Document.hideLabel) ? "show label" : "hide label",
+            event: this.setLabelField
+        });
+    }
+    @observable _panelWidth: number = 0;
+    @observable _panelHeight: number = 0;
     render() {
+        let labelField = StrCast(this.props.Document.labelField);
+        let hideLabel = BoolCast(this.props.Document.hideLabel);
+        let maxDoc = Cast(this.props.Document.maximizedDoc, Doc) as Doc;
+        let label = !hideLabel && maxDoc && labelField ? maxDoc[labelField] : "";
         return (
-            <div className="iconBox-container">
+            <div className="iconBox-container" onContextMenu={this.specificContextMenu}>
                 {this.minimizedIcon}
+                <Measure onResize={(r) => runInAction(() => { if (r.entry.width || BoolCast(this.props.Document.hideLabel)) this.props.Document.nativeWidth = this.props.Document.width = (r.entry.width + Number(MINIMIZED_ICON_SIZE)); })}>
+                    {({ measureRef }) =>
+                        <span ref={measureRef} className="iconBox-label">{label}</span>
+                    }
+                </Measure>
             </div>);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From f8f96fe4006f96459e6ad4b9889fdaefc339467e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 7 May 2019 13:57:57 -0400
Subject: fixed calling whenActiveChanged so you can drag text box when parent
 collection is active.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 17c25c9db..7fa945891 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -265,7 +265,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
             parentActive: this.props.active,
-            whenActiveChanged: this.props.active,
+            whenActiveChanged: this.props.whenActiveChanged,
             bringToFront: this.bringToFront,
         };
     }
@@ -274,7 +274,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     get views() {
         let curPage = FieldValue(this.Document.curPage, -1);
         let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => {
-            if (!FieldValue(doc)) return prev;
+            if (!(doc instanceof Doc)) return prev;
             var page = NumCast(doc.page, -1);
             if (page === curPage || page === -1) {
                 let minim = Cast(doc.isMinimized, "boolean");
-- 
cgit v1.2.3-70-g09d2

