From ba3f889a61715b715a230d0d24894a5951fbbb3b Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 21 Nov 2019 13:14:39 -0500
Subject: fixed inking on top of existing ink.  fixed zooming maximally out.

---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx     | 8 +++++---
 1 file changed, 5 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 3dd655b96..dc98e662d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -488,9 +488,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         let [x, y] = this.getTransform().transformPoint(pointX, pointY);
         let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-        let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
-        this.props.Document.scale = Math.abs(safeScale);
-        this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+        if (localTransform.Scale >= 0.15) {
+            let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
+            this.props.Document.scale = Math.abs(safeScale);
+            this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+        }
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From 17474b9b840c78df4dc2601e82cf63a85e0bcbf7 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 21 Nov 2019 13:57:54 -0500
Subject: added names to make mobx tracking easier for freeform reactions

---
 .../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 dc98e662d..8325ffe99 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -653,7 +653,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         }
     }
 
-    childDataProvider = computedFn((doc: Doc) => this._layoutPoolData.get(doc[Id]));
+    childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); });
 
     doPivotLayout(poolData: ObservableMap<string, any>) {
         return computePivotLayout(poolData, this.props.Document, this.childDocs,
@@ -697,7 +697,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     componentDidMount() {
         this._layoutComputeReaction = reaction(() => { trace(); return this.doLayoutComputation },
             action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)),
-            { fireImmediately: true });
+            { fireImmediately: true, name: "doLayout" });
     }
     componentWillUnmount() {
         this._layoutComputeReaction && this._layoutComputeReaction();
-- 
cgit v1.2.3-70-g09d2


From 7ef52a87d1731770c6e1a8cd1aef31cb384fff05 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 21 Nov 2019 16:27:56 -0500
Subject: made textbox sidebar toggle a widget.  fixed isAnimating stuff.  made
 TraceMobx to simplify tracing

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 16 +++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 13 ++---
 src/client/views/nodes/DocumentView.tsx            |  7 ++-
 src/client/views/nodes/FormattedTextBox.scss       | 24 +++++++-
 src/client/views/nodes/FormattedTextBox.tsx        | 66 +++++++++++++---------
 src/client/views/nodes/ImageBox.tsx                |  3 +-
 src/client/views/pdf/PDFViewer.tsx                 |  5 +-
 src/new_fields/documentSchemas.ts                  |  2 +-
 src/new_fields/util.ts                             |  6 +-
 9 files changed, 89 insertions(+), 53 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 8325ffe99..b2344771d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -40,6 +40,7 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { computedFn, keepAlive } from "mobx-utils";
+import { TraceMobx } from "../../../../new_fields/util";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -621,12 +622,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } {
-        const script = this.Document.arrangeScript;
-        const result = script && script.script.run(params, console.log);
-        const layoutDoc = Doc.Layout(params.doc);
-        if (result && result.success) {
+        const result = this.Document.arrangeScript?.script.run(params, console.log);
+        if (result?.success) {
             return { ...result, transition: "transform 1s" };
         }
+        const layoutDoc = Doc.Layout(params.doc);
         return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc.width, "number"), height: Cast(layoutDoc.height, "number") };
     }
 
@@ -653,7 +653,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         }
     }
 
-    childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); });
+    childDataProvider = computedFn(function childDataProvider(doc: Doc) { return (this as any)._layoutPoolData.get(doc[Id]); }.bind(this));
 
     doPivotLayout(poolData: ObservableMap<string, any>) {
         return computePivotLayout(poolData, this.props.Document, this.childDocs,
@@ -667,9 +667,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         let elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
 
         this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
+            const data = poolData.get(pair.layout[Id]);
             const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state });
             state = pos.state === undefined ? state : pos.state;
-            let data = this._layoutPoolData.get(pair.layout[Id]);
             if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height || pos.transition !== data.transition) {
                 runInAction(() => poolData.set(pair.layout[Id], pos));
             }
@@ -695,7 +695,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     componentDidMount() {
-        this._layoutComputeReaction = reaction(() => { trace(); return this.doLayoutComputation },
+        this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation },
             action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)),
             { fireImmediately: true, name: "doLayout" });
     }
@@ -845,7 +845,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         return eles;
     }
     render() {
-        trace();
+        TraceMobx();
         // update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
         // this.Document.fitX = this.contentBounds && this.contentBounds.x;
         // this.Document.fitY = this.contentBounds && this.contentBounds.y;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 0badbd3fe..c85b59488 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -10,6 +10,7 @@ import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
 import { PositionDocument } from "../../../new_fields/documentSchemas";
+import { TraceMobx } from "../../../new_fields/util";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined;
@@ -56,11 +57,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         this._disposer && this._disposer();
     }
     componentDidMount() {
-        this._disposer = reaction(() => [this.props.Document.animateToPos, this.props.Document.isAnimating],
-            () => {
-                const target = this.props.Document.animateToPos ? Array.from(Cast(this.props.Document.animateToPos, listSpec("number"))!) : undefined;
-                this._animPos = !target ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]);
-            }, { fireImmediately: true });
+        this._disposer = reaction(() => this.props.Document.animateToPos ? Array.from(Cast(this.props.Document.animateToPos, listSpec("number"))!) : undefined,
+            target => this._animPos = !target ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] : this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]),
+            { fireImmediately: true });
     }
 
     contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect ? this.width / this.nativeWidth : 1;
@@ -88,7 +87,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     finalPanelHeight = () => this.dataProvider ? this.dataProvider.height : this.panelHeight();
 
     render() {
-        trace();
+        TraceMobx();
         return <div className="collectionFreeFormDocumentView-container"
             style={{
                 boxShadow:
@@ -99,7 +98,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                                     StrCast(this.layoutDoc.boxShadow, ""),
                 borderRadius: this.borderRounding(),
                 transform: this.transform,
-                transition: this.Document.isAnimating !== undefined ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
+                transition: this.Document.isAnimating ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
                 width: this.width,
                 height: this.height,
                 zIndex: this.Document.zIndex || 0,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a985f3069..9ec339686 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -43,6 +43,7 @@ import React = require("react");
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { InkingControl } from '../InkingControl';
 import { InkTool } from '../../../new_fields/InkField';
+import { TraceMobx } from '../../../new_fields/util';
 
 library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
     fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -542,7 +543,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get finalLayoutKey() { return this.props.layoutKey || "layout"; }
     childScaling = () => (this.layoutDoc.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
     @computed get contents() {
-        trace();
+        TraceMobx();
         return (<DocumentContentsView ContainingCollectionView={this.props.ContainingCollectionView}
             ContainingCollectionDoc={this.props.ContainingCollectionDoc}
             Document={this.props.Document}
@@ -586,7 +587,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     @computed get innards() {
-        trace();
+        TraceMobx();
         const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
         const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle");
         const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption");
@@ -669,7 +670,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
             onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
             style={{
-                transition: this.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.Document.transition),
+                transition: this.Document.isAnimating ? ".5s linear" : StrCast(this.Document.transition),
                 pointerEvents: this.ignorePointerEvents ? "none" : "all",
                 color: StrCast(this.Document.color),
                 outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 269a3ca68..77cdd3d42 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -27,6 +27,8 @@
     pointer-events: all;
     overflow-y: auto;
     max-height: 100%;
+    display: flex;
+    flex-direction: row;
 
     .formattedTextBox-dictation {
         height: 20px;
@@ -48,10 +50,28 @@
     width: 100%;
     height: 100%;
 }
-.formattedTextBox-sidebar,.formattedTextBox-sidebar-inking {
-    border-left: solid 1px black; 
+.formattedTextBox-sidebar-handle {
+    position: absolute;
+    top: calc(50% - 17.5px);
+    width: 10px;
+    height: 35px;
+    background: lightgray;
+    border-radius: 20px;
+}
+.formattedTextBox-cont > .formattedTextBox-sidebar-handle {
+    right: 0;
+    left: unset;
+}
+.formattedTextBox-sidebar, .formattedTextBox-sidebar-inking {
+    border-left: dashed 1px black; 
     height: 100%; 
     display: inline-block;
+    position: absolute;
+    right: 0;
+    > .formattedTextBox-sidebar-handle {
+        right:unset;
+        left:-5;
+    }
 }
 
 .formattedTextBox-sidebar-inking {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 5201f9bdc..86a32eb22 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -47,6 +47,7 @@ import { documentSchema } from '../../../new_fields/documentSchemas';
 import { AudioBox } from './AudioBox';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { InkTool } from '../../../new_fields/InkField';
+import { TraceMobx } from '../../../new_fields/util';
 
 library.add(faEdit);
 library.add(faSmile, faTextHeight, faUpload);
@@ -358,9 +359,11 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         }
     }
 
+    toggleSidebar = () => this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%";
+
     specificContextMenu = (e: React.MouseEvent): void => {
         let funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.props.Document.sidebarWidthPercent = StrCast(this.props.Document.sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%"; }, icon: "expand-arrows-alt" });
+        funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.toggleSidebar() }, icon: "expand-arrows-alt" });
         funcs.push({ description: "Record Bullet", event: () => { e.stopPropagation(); this.recordBullet(); }, icon: "expand-arrows-alt" });
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
             funcs.push({
@@ -997,13 +1000,16 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
         if (!this._undoTyping) {
             this._undoTyping = UndoManager.StartBatch("undoTyping");
         }
-        if (this._recording) { this.stopDictation(true); setTimeout(() => this.recordDictation(), 250); }
+        if (this._recording) {
+            this.stopDictation(true);
+            setTimeout(() => this.recordDictation(), 250);
+        }
     }
 
     @action
     tryUpdateHeight() {
-        let scrollHeight = this._ref.current ? this._ref.current.scrollHeight : 0;
-        if (!this.layoutDoc.isAnimating && this.layoutDoc.autoHeight && scrollHeight !== 0 &&
+        const scrollHeight = this._ref.current?.scrollHeight;
+        if (!this.layoutDoc.animateToPos && this.layoutDoc.autoHeight && scrollHeight &&
             getComputedStyle(this._ref.current!.parentElement!).top === "0px") {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
             let nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
             let dh = NumCast(this.layoutDoc.height, 0);
@@ -1016,7 +1022,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     @computed get sidebarWidth() { return Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); }
     @computed get annotationsKey() { return "annotations"; }
     render() {
-        trace();
+        TraceMobx();
         let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
         let interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
         if (this.props.isSelected()) {
@@ -1050,29 +1056,33 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }}>
                     <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
                 </div>
-                {this.sidebarWidthPercent === "0%" ? (null) : <div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}` }}>
-                    <CollectionFreeFormView {...this.props}
-                        PanelHeight={() => this.props.PanelHeight()}
-                        PanelWidth={() => this.sidebarWidth}
-                        annotationsKey={this.annotationsKey}
-                        isAnnotationOverlay={true}
-                        focus={this.props.focus}
-                        isSelected={this.props.isSelected}
-                        select={emptyFunction}
-                        active={this.annotationsActive}
-                        ContentScaling={returnOne}
-                        whenActiveChanged={this.whenActiveChanged}
-                        removeDocument={this.removeDocument}
-                        moveDocument={this.moveDocument}
-                        addDocument={this.addDocument}
-                        CollectionView={undefined}
-                        ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)}
-                        ruleProvider={undefined}
-                        renderDepth={this.props.renderDepth + 1}
-                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                        chromeCollapsed={true}>
-                    </CollectionFreeFormView>
-                </div>}
+                {this.sidebarWidthPercent === "0%" ?
+                    <div className="formattedTextBox-sidebar-handle" onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} /> :
+                    <div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")}
+                        style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${StrCast(this.extensionDoc?.backgroundColor, "transparent")}` }}>
+                        <CollectionFreeFormView {...this.props}
+                            PanelHeight={() => this.props.PanelHeight()}
+                            PanelWidth={() => this.sidebarWidth}
+                            annotationsKey={this.annotationsKey}
+                            isAnnotationOverlay={true}
+                            focus={this.props.focus}
+                            isSelected={this.props.isSelected}
+                            select={emptyFunction}
+                            active={this.annotationsActive}
+                            ContentScaling={returnOne}
+                            whenActiveChanged={this.whenActiveChanged}
+                            removeDocument={this.removeDocument}
+                            moveDocument={this.moveDocument}
+                            addDocument={this.addDocument}
+                            CollectionView={undefined}
+                            ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)}
+                            ruleProvider={undefined}
+                            renderDepth={this.props.renderDepth + 1}
+                            ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                            chromeCollapsed={true}>
+                        </CollectionFreeFormView>
+                        <div className="formattedTextBox-sidebar-handle" onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} />
+                    </div>}
                 <div className="formattedTextBox-dictation"
                     onClick={e => {
                         this._recording ? this.stopDictation(true) : this.recordDictation();
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5d40e274f..d102c9600 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -27,6 +27,7 @@ import React = require("react");
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
+import { TraceMobx } from '../../../new_fields/util';
 var requestImageSize = require('../../util/request-image-size');
 var path = require('path');
 const { Howl } = require('howler');
@@ -268,7 +269,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     }
 
     @computed get content() {
-        trace();
+        TraceMobx();
         const extensionDoc = this.extensionDoc;
         if (!extensionDoc) return (null);
         // let transform = this.props.ScreenToLocalTransform().inverse();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 22c3a29a8..913aaa087 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -29,6 +29,7 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { InkTool } from "../../../new_fields/InkField";
+import { TraceMobx } from "../../../new_fields/util";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
 
@@ -620,7 +621,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     }
 
     @computed get annotationLayer() {
-        trace();
+        TraceMobx();
         return <div className="pdfViewer-annotationLayer" style={{ height: (this.Document.nativeHeight || 0), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
             {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
                 <Annotation {...this.props} focus={this.props.focus} extensionDoc={this.extensionDoc!} anno={anno} key={`${anno[Id]}-annotation`} />)}
@@ -674,7 +675,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     contentZoom = () => this._zoomed;
     @computed get contentScaling() { return this.props.ContentScaling() }
     render() {
-        trace();
+        TraceMobx();
         return !this.extensionDoc ? (null) :
             <div className={"pdfViewer-viewer" + (this._zoomed !== 1 ? "-zoomed" : "")} ref={this._mainCont}
                 style={{
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 7592cdaa3..4c2f061a6 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -41,7 +41,7 @@ export const documentSchema = createSchema({
     showTitle: "string",        // whether an editable title banner is displayed at tht top of the document
     isButton: "boolean",        // whether document functions as a button (overiding native interactions of its content)    
     ignoreClick: "boolean",     // whether documents ignores input clicks (but does not ignore manipulation and other events) 
-    isAnimating: "boolean",     // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents).
+    isAnimating: "string",      // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents).  value is undefined|"min"|"max"
     animateToDimensions: listSpec("number"), // layout information about the target rectangle a document is animating towards 
     scrollToLinkID: "string",   // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
     strokeWidth: "number",
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 04194509c..4147be278 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -4,7 +4,7 @@ import { SerializationHelper } from "../client/util/SerializationHelper";
 import { ProxyField } from "./Proxy";
 import { RefField } from "./RefField";
 import { ObjectField } from "./ObjectField";
-import { action } from "mobx";
+import { action, trace } from "mobx";
 import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
 import { DocServer } from "../client/DocServer";
 
@@ -12,6 +12,10 @@ function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
 }
 
+export function TraceMobx() {
+    //trace();
+}
+
 export interface GetterResult {
     value: FieldResult;
     shouldReturn?: boolean;
-- 
cgit v1.2.3-70-g09d2


From 3b37cc31bb09b11238868c34a38a8e99f508479f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 22 Nov 2019 13:22:21 -0500
Subject: fixed link anchors to move to be nearest alternate anchor.

---
 src/Utils.ts                                       | 57 ++++++++++++++++++++++
 src/client/views/Touchable.tsx                     |  2 +-
 .../views/collections/ParentDocumentSelector.tsx   |  1 +
 .../CollectionFreeFormLinkView.tsx                 | 55 +++++++++++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 15 ++++--
 src/client/views/nodes/FontIconBox.tsx             |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 src/client/views/nodes/ImageBox.scss               |  1 +
 src/client/views/nodes/ImageBox.tsx                |  4 +-
 src/client/views/nodes/VideoBox.scss               |  1 +
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 13 files changed, 119 insertions(+), 27 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 12acda4bb..37b509370 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -153,6 +153,63 @@ export namespace Utils {
         return Math.max(lower, Math.min(upper, n));
     }
 
+    export function distanceBetweenHorizontalLines(xs: number, xe: number, y: number, xs2: number, xe2: number, y2: number): [number, number[]] {
+        if ((xs2 < xs && xe2 > xs) || (xs2 < xe && xe2 > xe) || (xs2 > xs && xe2 < xe)) return [Math.abs(y - y2), [Math.max(xs, xs2), y, Math.min(xe, xe2), y]];
+        if (xe2 < xs) return [Math.sqrt((xe2 - xs) * (xe2 - xs) + (y2 - y) * (y2 - y)), [xs, y, xs, y]];
+        //if (xs2 > xe) 
+        return [Math.sqrt((xs2 - xe) * (xs2 - xe) + (y2 - y) * (y2 - y)), [xe, y, xe, y]];
+    }
+    export function distanceBetweenVerticalLines(x: number, ys: number, ye: number, x2: number, ys2: number, ye2: number): [number, number[]] {
+        if ((ys2 < ys && ye2 > ys) || (ys2 < ye && ye2 > ye) || (ys2 > ys && ye2 < ye)) return [Math.abs(x - x2), [x, Math.max(ys, ys2), x, Math.min(ye, ye2)]];
+        if (ye2 < ys) return [Math.sqrt((ye2 - ys) * (ye2 - ys) + (x2 - x) * (x2 - x)), [x, ys, x, ys]];
+        //if (ys2 > ye) 
+        return [Math.sqrt((ys2 - ye) * (ys2 - ye) + (x2 - x) * (x2 - x)), [x, ye, x, ye]];
+    }
+
+    function project(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
+
+        if (ax === bx && ay === by) return { point: { x: ax, y: ay }, left: false, dot: 0, t: 0 };
+        var atob = { x: bx - ax, y: by - ay };
+        var atop = { x: px - ax, y: py - ay };
+        var len = atob.x * atob.x + atob.y * atob.y;
+        var dot = atop.x * atob.x + atop.y * atob.y;
+        var t = Math.min(1, Math.max(0, dot / len));
+
+        dot = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
+
+        return {
+            point: {
+                x: ax + atob.x * t,
+                y: ay + atob.y * t
+            },
+            left: dot < 1,
+            dot: dot,
+            t: t
+        };
+    }
+
+    export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number,
+        l1: number, t1: number, w1: number, h1: number,
+        x: number, y: number) {
+        var r = l + w,
+            b = t + h;
+        var r1 = l1 + w1,
+            b1 = t1 + h1;
+        let hsegs = [[l, r, t, l1, r1, t1], [l, r, b, l1, r1, t1], [l, r, t, l1, r1, b1], [l, r, b, l1, r1, b1]];
+        let vsegs = [[l, t, b, l1, t1, b1], [r, t, b, l1, t1, b1], [l, t, b, r1, t1, b1], [r, t, b, r1, t1, b1]];
+        let res = hsegs.reduce((closest, seg) => {
+            let res = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+            return (res[0] < closest[0]) ? res : closest;
+        }, [Number.MAX_VALUE, []] as [number, number[]]);
+        let fres = vsegs.reduce((closest, seg) => {
+            let res = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+            return (res[0] < closest[0]) ? res : closest;
+        }, res);
+
+        let near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
+        return project(near.point.x, near.point.y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
+    }
+
     export function getNearestPointInPerimeter(l: number, t: number, w: number, h: number, x: number, y: number) {
         var r = l + w,
             b = t + h;
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index ba87025c4..0dd4f734c 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -45,7 +45,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         this._touchDrag = true;
         switch (e.targetTouches.length) {
             case 1:
-                this.handle1PointerMove(e)
+                this.handle1PointerMove(e);
                 break;
             case 2:
                 this.handle2PointersMove(e);
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index ba83630a4..4eb9e9d1e 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -14,6 +14,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { faEdit } from "@fortawesome/free-solid-svg-icons";
 import { library } from "@fortawesome/fontawesome-svg-core";
 import { MetadataEntryMenu } from "../MetadataEntryMenu";
+import { DocumentView } from "../nodes/DocumentView";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 837413842..73b45edc6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -6,7 +6,8 @@ import "./CollectionFreeFormLinkView.scss";
 import React = require("react");
 import v5 = require("uuid/v5");
 import { DocumentType } from "../../../documents/DocumentTypes";
-import { observable, action } from "mobx";
+import { observable, action, reaction, IReactionDisposer } from "mobx";
+import { StrCast, Cast } from "../../../../new_fields/Types";
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -16,33 +17,57 @@ export interface CollectionFreeFormLinkViewProps {
 
 @observer
 export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
-    @observable _alive: number = 0;
     @observable _opacity: number = 1;
+    @observable _update: number = 0;
+    _anchorDisposer: IReactionDisposer | undefined;
     @action
     componentDidMount() {
-        this._alive = 1;
-        setTimeout(this.rerender, 50);
-        setTimeout(action(() => this._opacity = 0.05), 50);
+        setTimeout(action(() => this._opacity = 0.05), 750);
+        this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform()],
+            () => {
+                let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+                let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+                let adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
+                let bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv!);
+                let a = adiv.getBoundingClientRect();
+                let b = bdiv.getBoundingClientRect();
+                let abounds = adiv.parentElement!.getBoundingClientRect();
+                let bbounds = bdiv.parentElement!.getBoundingClientRect();
+                let apt = Utils.closestPtBetweenRectangles(abounds.left, abounds.top, abounds.width, abounds.height,
+                    bbounds.left, bbounds.top, bbounds.width, bbounds.height,
+                    a.left + a.width / 2, a.top + a.height / 2);
+                let bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height,
+                    abounds.left, abounds.top, abounds.width, abounds.height,
+                    apt.point.x, apt.point.y);
+                let afield = StrCast(this.props.A.props.Document[StrCast(this.props.A.props.layoutKey, "layout")]).indexOf("anchor1") === -1 ? "anchor2" : "anchor1";
+                let bfield = afield === "anchor1" ? "anchor2" : "anchor1";
+                this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
+                this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
+                this.props.A.props.Document[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
+                this.props.A.props.Document[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100;
+                this._update++;
+            }
+            , { fireImmediately: true });
     }
     @action
     componentWillUnmount() {
-        this._alive = 0;
+        this._anchorDisposer?.();
     }
-    rerender = action(() => {
-        if (this._alive) {
-            setTimeout(this.rerender, 50);
-            this._alive++;
-        }
-    });
 
     render() {
-        let y = this._alive;
+        let y = this._update;
         let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
         let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
         let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
         let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
-        let pt1 = Utils.getNearestPointInPerimeter(a.left, a.top, a.width, a.height, b.left + b.width / 2, b.top + b.height / 2);
-        let pt2 = Utils.getNearestPointInPerimeter(b.left, b.top, b.width, b.height, a.left + a.width / 2, a.top + a.height / 2);
+        let apt = Utils.closestPtBetweenRectangles(a.left, a.top, a.width, a.height,
+            b.left, b.top, b.width, b.height,
+            a.left + a.width / 2, a.top + a.height / 2);
+        let bpt = Utils.closestPtBetweenRectangles(b.left, b.top, b.width, b.height,
+            a.left, a.top, a.width, a.height,
+            apt.point.x, apt.point.y);
+        let pt1 = [apt.point.x, apt.point.y];
+        let pt2 = [bpt.point.x, bpt.point.y];
         return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
             style={{ opacity: this._opacity }}
             x1={`${pt1[0]}`} y1={`${pt1[1]}`}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b2344771d..9bbaa20e7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -695,7 +695,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     }
 
     componentDidMount() {
-        this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation },
+        this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation; },
             action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)),
             { fireImmediately: true, name: "doLayout" });
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9ec339686..467fd4b32 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -617,9 +617,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 />
             </div>);
         return <>
-            {this.Document.links && DocListCast(this.Document.links).filter((d) => !DocListCast(this.layoutDoc.hiddenLinks).some(hidden => Doc.AreProtosEqual(hidden, d))).filter(this.isNonTemporalLink).map((d, i) =>
-                <div className="documentView-docuLinkWrapper" key={`${d[Id]}`} style={{ transform: `scale(${this.layoutDoc.fitWidth ? 1 : 1 / 1})` }}>
-                    <DocumentView {...this.props} ContentScaling={returnOne} Document={d} layoutKey={this.linkEndpoint(d)} backgroundColor={returnTransparent} removeDocument={undoBatch(doc => Doc.AddDocToList(this.layoutDoc, "hiddenLinks", doc))} />
+            {this.Document.links && DocListCast(this.Document.links).filter(d => !d.hidden).filter(this.isNonTemporalLink).map((d, i) =>
+                <div className="documentView-docuLinkWrapper" key={`${d[Id]}`}>
+                    <DocumentView {...this.props} ContentScaling={returnOne} Document={d} layoutKey={this.linkEndpoint(d)} backgroundColor={returnTransparent} removeDocument={undoBatch(doc => doc.hidden = true)} />
                 </div>)}
             {!showTitle && !showCaption ?
                 this.Document.searchFields ?
@@ -668,7 +668,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
         return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont}
             onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
-            onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
+            onPointerEnter={e => {
+                console.log("Brush" + this.props.Document.title);
+                Doc.BrushDoc(this.props.Document);
+            }} onPointerLeave={e => {
+                console.log("UnBrush" + this.props.Document.title);
+                Doc.UnBrushDoc(this.props.Document);
+
+            }}
             style={{
                 transition: this.Document.isAnimating ? ".5s linear" : StrCast(this.Document.transition),
                 pointerEvents: this.ignorePointerEvents ? "none" : "all",
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 9a5de836f..960b55e3e 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -43,7 +43,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
                 background: StrCast(referenceLayout.backgroundColor),
                 boxShadow: this.props.Document.ischecked ? `4px 4px 12px black` : undefined
             }}>
-            <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} color={this._foregroundColor} size="md" />
+            <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} color={this._foregroundColor} size="sm" />
         </button>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 86a32eb22..9910c9ecd 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -363,7 +363,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     specificContextMenu = (e: React.MouseEvent): void => {
         let funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.toggleSidebar() }, icon: "expand-arrows-alt" });
+        funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.toggleSidebar(); }, icon: "expand-arrows-alt" });
         funcs.push({ description: "Record Bullet", event: () => { e.stopPropagation(); this.recordBullet(); }, icon: "expand-arrows-alt" });
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
             funcs.push({
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index dcecbdc6e..ba4ef8879 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -16,6 +16,7 @@
     width:100%;
     height:100%;
     position: absolute;
+    transform-origin: top left;
 }
 
 .imageBox-cont-interactive {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d102c9600..f21ce3bf2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -292,7 +292,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
         if (field instanceof ImageField) paths = [this.choosePath(field.url)];
         paths.push(...altpaths);
         // }
-        let interactive = InkingControl.Instance.selectedTool || this.Document.isBackground ? "" : "-interactive";
+        let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
         let rotation = NumCast(this.Document.rotation, 0);
         let aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
         let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
@@ -335,7 +335,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     contentFunc = () => [this.content];
     render() {
         return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}
-            style={{ transformOrigin: "top left", transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+            style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
             <CollectionFreeFormView {...this.props}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 5829c1bd9..0a4c650a8 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,5 +1,6 @@
 .videoBox-container {
     pointer-events: all;
+    transform-origin: top left;
     .inkingCanvas-paths-markers {
         opacity : 0.4; // we shouldn't have to do this, but since chrome crawls to a halt with z-index unset in videoBox-content, this is a workaround
     }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index d70f0474c..bd5bd918f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -336,7 +336,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
     contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
     render() {
         return (<div className={"videoBox-container"} onContextMenu={this.specificContextMenu}
-            style={{ transformOrigin: "top left", transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+            style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
             <CollectionFreeFormView {...this.props}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 913aaa087..f1c500391 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -673,7 +673,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
     marqueeing = () => this._marqueeing;
     visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96;
     contentZoom = () => this._zoomed;
-    @computed get contentScaling() { return this.props.ContentScaling() }
+    @computed get contentScaling() { return this.props.ContentScaling(); }
     render() {
         TraceMobx();
         return !this.extensionDoc ? (null) :
-- 
cgit v1.2.3-70-g09d2

