From 5338dfd24d0f6a65bb3131332746c1d2c82ef7f2 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 4 Apr 2020 15:23:42 -0400
Subject: fix to presentationView opacity. removed getScale and zoomToScale
 props which weren't being used.

---
 src/client/views/nodes/AudioBox.tsx | 2 --
 1 file changed, 2 deletions(-)

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

diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index bd54d64ff..627a71d67 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -265,8 +265,6 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                                                 ContainingCollectionDoc={this.props.Document}
                                                 parentActive={returnTrue}
                                                 bringToFront={emptyFunction}
-                                                zoomToScale={emptyFunction}
-                                                getScale={returnOne}
                                                 backgroundColor={returnTransparent} />
                                         </div>
                                         <div key={i} className="audiobox-marker" onPointerEnter={() => Doc.linkFollowHighlight(la1)}
-- 
cgit v1.2.3-70-g09d2


From 50257b6f3ea91f9c8ac63985c9d81cb7546d9541 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 5 Apr 2020 01:45:53 -0400
Subject: more cleanup to link following.  got rid of unneeded link doc fields
 _context & _timecode

---
 src/client/documents/Documents.ts                  |  2 -
 src/client/util/DocumentManager.ts                 | 79 ++++++++++------------
 src/client/views/nodes/AudioBox.tsx                | 11 +--
 src/client/views/nodes/DocumentView.tsx            | 33 +++++----
 src/client/views/nodes/FormattedTextBoxComment.tsx |  2 +-
 src/client/views/pdf/Annotation.tsx                |  4 +-
 src/new_fields/util.ts                             |  2 +-
 7 files changed, 65 insertions(+), 68 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 514200d95..c9d7fac33 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -564,8 +564,6 @@ export namespace Docs {
             const linkDocProto = Doc.GetProto(doc);
             linkDocProto.anchor1 = source.doc;
             linkDocProto.anchor2 = target.doc;
-            linkDocProto.anchor1_timecode = source.doc.currentTimecode || source.doc.displayTimecode;
-            linkDocProto.anchor2_timecode = target.doc.currentTimecode || source.doc.displayTimecode;
 
             if (linkDocProto.layout_key1 === undefined) {
                 Cast(linkDocProto.proto, Doc, null).layout_key1 = DocuLinkBox.LayoutString("anchor1");
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ae392a35b..7b8fb9e4c 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,6 @@
 import { action, computed, observable } from 'mobx';
 import { Doc, DocListCastAsync, DocListCast, Opt } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
-import { List } from '../../new_fields/List';
 import { Cast, NumCast, StrCast } from '../../new_fields/Types';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { CollectionView } from '../views/collections/CollectionView';
@@ -10,8 +9,8 @@ import { LinkManager } from './LinkManager';
 import { Scripting } from './Scripting';
 import { SelectionManager } from './SelectionManager';
 import { DocumentType } from '../documents/DocumentTypes';
-import { UndoManager } from './UndoManager';
 
+export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
 
 export class DocumentManager {
 
@@ -137,14 +136,15 @@ export class DocumentManager {
     public jumpToDocument = async (
         targetDoc: Doc,
         willZoom: boolean,
-        dockFunc = DocumentManager.addRightSplit,
+        createViewFunc = DocumentManager.addRightSplit,
         docContext?: Doc,
         linkId?: string,
         closeContextIfNotFound: boolean = false,
         originatingDoc: Opt<Doc> = undefined,
-        finished?: () => void): Promise<void> => {
+        finished?: () => void
+    ): Promise<void> => {
         const getFirstDocView = DocumentManager.Instance.getFirstDocumentView;
-        const finishFalse: DocFocusFunc = () => { finished?.(); return false; };
+        const focusAndFinish = () => { finished?.(); return false; };
         const highlight = () => {
             const finalDocView = getFirstDocView(targetDoc);
             if (finalDocView) {
@@ -159,7 +159,7 @@ export class DocumentManager {
             if (first) annotatedDoc = first.props.Document;
         }
         if (docView) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?
-            docView.props.focus(docView.props.Document, willZoom, undefined, finishFalse);
+            docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
             highlight();
         } else {
             const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
@@ -167,72 +167,67 @@ export class DocumentManager {
             const targetDocContext = annotatedDoc || contextDoc;
 
             if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
-                dockFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
+                createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
                 highlight();
-            } else {
+            } else {  // otherwise try to get a view of the context of the target
                 const targetDocContextView = getFirstDocView(targetDocContext);
                 targetDocContext.scrollY = 0;  // this will force PDFs to activate and load their annotations / allow scrolling
-                if (targetDocContextView) { // we have a context view and aren't forced to create a new one ... focus on the context
+                if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
                     targetDocContext.panTransformType = "Ease";
                     targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom);
 
                     // now find the target document within the context
-                    if (targetDoc.displayTimecode) {  // the target should show up once the video scrubs to the display timecode;
+                    if (targetDoc.displayTimecode) {  // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
                         targetDocContext.currentTimecode = targetDoc.displayTimecode;
                         finished?.();
-                    } else {
+                    } else { // no timecode means we need to find the context view and focus on our target
                         setTimeout(() => {
-                            const retryDocView = getFirstDocView(targetDoc);
-                            if (retryDocView) {
-                                retryDocView.props.focus(targetDoc, willZoom, undefined, finishFalse); // focus on the target if it now exists in the context
-                            } else {
+                            const retryDocView = getFirstDocView(targetDoc);  // test again for the target view snce we presumably created the context above by focusing on it
+                            if (retryDocView) {   // we found the target in the context
+                                retryDocView.props.focus(targetDoc, willZoom, undefined, focusAndFinish); // focus on the target in the context
+                            } else { // we didn't find the target, so it must have moved out of the context.  Go back to just creating it.
                                 if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.props.Document);
-                                targetDoc.layout && dockFunc(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), finished); // otherwise create a new view of the target
+                                targetDoc.layout && createViewFunc(Doc.BrushDoc(targetDoc), finished); //  create a new view of the target
                             }
                             highlight();
                         }, 0);
                     }
                 } else {  // there's no context view so we need to create one first and try again
-                    dockFunc(targetDocContext);
+                    createViewFunc(targetDocContext); // so first we create the target, but don't pass finished because we still need to create the target
                     setTimeout(() => {
                         const finalDocView = getFirstDocView(targetDoc);
                         const finalDocContextView = getFirstDocView(targetDocContext);
                         setTimeout(() =>  // if not, wait a bit to see if the context can be loaded (e.g., a PDF). wait interval heurisitic tries to guess how we're animating based on what's just become visible
-                            this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, linkId, true, undefined, finished), finalDocView ? 0 : finalDocContextView ? 250 : 2000); // so call jump to doc again and if the doc isn't found, it will be created.
+                            this.jumpToDocument(targetDoc, willZoom, createViewFunc, undefined, linkId, true, undefined, finished), // pass true this time for closeContextIfNotFound
+                            finalDocView ? 0 : finalDocContextView ? 250 : 2000); // so call jump to doc again and if the doc isn't found, it will be created.
                     }, 0);
                 }
             }
         }
     }
 
-    public async FollowLink(link: Doc | undefined, doc: Doc, focus: (doc: Doc, followLinkLocation: string, afterFocus?: DocFocusFunc) => void, zoom = false, reverse = false, currentContext?: Doc, finished?: () => void) {
+    public async FollowLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
         const linkDocs = link ? [link] : DocListCast(doc.links);
         SelectionManager.DeselectAll();
-        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc));
-        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc));
-        const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
-        const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
-        const first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs;
-        const second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs;
-        const linkDoc = first.length ? first[0] : second.length ? second[0] : undefined;
-        const linkFollowDocs = first.length ? [await first[0].anchor2 as Doc, await first[0].anchor1 as Doc] : second.length ? [await second[0].anchor1 as Doc, await second[0].anchor2 as Doc] : undefined;
-        const linkFollowDocContexts = first.length ? [await first[0].context as Doc, await first[0].context as Doc] : second.length ? [await second[0].context as Doc, await second[0].context as Doc] : [undefined, undefined];
-        const linkFollowTimecodes = first.length ? [NumCast(first[0].anchor2_timecode), NumCast(first[0].anchor1_timecode)] : second.length ? [NumCast(second[0].anchor1_timecode), NumCast(second[0].anchor2_timecode)] : [undefined, undefined];
-        const Jump = DocumentManager.Instance.jumpToDocument;
-        const Focus = (where: string) => (doc: Doc, finished?: () => void) => focus(doc, where, () => { finished?.(); return false; });
-        if (linkFollowDocs && linkDoc) {
-            const followLinkLocation = StrCast(linkDoc.followLinkLocation, "inTab");
-            const targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined;
-            const target = linkFollowDocs[reverse ? 1 : 0];
-            const annotatedDoc = await Cast(target.annotationOn, Doc);
-            if (annotatedDoc) {
-                annotatedDoc.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]);
+        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc)); // link docs where 'doc' is anchor1
+        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc)); // link docs where 'doc' is anchor2
+        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+        const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
+        const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
+        const linkDoc = linkDocList.length && linkDocList[0];
+        if (linkDoc) {
+            const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
+                (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
+            if (target) {
+                const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
+                containerDoc.currentTimecode !== undefined && (containerDoc.currentTimecode = NumCast(target?.timecode));
+                const targetContext = await target?.context as Doc;
+                const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
+                DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, "onRight", finished), targetNavContext, linkDoc[Id], undefined, doc, finished);
             } else {
-                target.currentTimecode !== undefined && (target.currentTimecode = linkFollowTimecodes[reverse ? 1 : 0]);
+                finished?.();
             }
-            Jump(linkFollowDocs[reverse ? 1 : 0], zoom, Focus(followLinkLocation), targetContext, linkDoc[Id], undefined, doc, finished);
-        } else if (link) {
-            Jump(link, zoom, Focus("onRight"), undefined, undefined, undefined, undefined, finished);
         } else {
             finished?.();
         }
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 627a71d67..be0e1aec4 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -71,7 +71,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             scrollLinkId => {
                 if (scrollLinkId) {
                     DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
-                        const linkTime = Doc.AreProtosEqual(l.anchor1 as Doc, this.dataDoc) ? NumCast(l.anchor1_timecode) : NumCast(l.anchor2_timecode);
+                        const linkTime = Doc.AreProtosEqual(l.anchor1 as Doc, this.dataDoc) ? NumCast((l.anchor1 as Doc).timecode) : NumCast((l.anchor2 as Doc).timecode);
                         setTimeout(() => { this.playFromTime(linkTime); Doc.linkFollowHighlight(l); }, 250);
                     });
                     Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
@@ -92,10 +92,11 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
             DocListCast(this.dataDoc.links).map(l => {
                 let la1 = l.anchor1 as Doc;
-                let linkTime = NumCast(l.anchor2_timecode);
+                const la2 = l.anchor2 as Doc;
+                let linkTime = NumCast(la2.timecode);
                 if (Doc.AreProtosEqual(la1, this.dataDoc)) {
+                    linkTime = NumCast(la1.timecode);
                     la1 = l.anchor2 as Doc;
-                    linkTime = NumCast(l.anchor1_timecode);
                 }
                 if (linkTime > NumCast(this.Document.currentTimecode) && linkTime < htmlEle.currentTime) {
                     Doc.linkFollowHighlight(la1);
@@ -249,11 +250,11 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                             {DocListCast(this.dataDoc.links).map((l, i) => {
                                 let la1 = l.anchor1 as Doc;
                                 let la2 = l.anchor2 as Doc;
-                                let linkTime = NumCast(l.anchor2_timecode);
+                                let linkTime = NumCast(la2.timecode);
                                 if (Doc.AreProtosEqual(la1, this.dataDoc)) {
                                     la1 = l.anchor2 as Doc;
                                     la2 = l.anchor1 as Doc;
-                                    linkTime = NumCast(l.anchor1_timecode);
+                                    linkTime = NumCast(la1.timecode);
                                 }
                                 return !linkTime ? (null) :
                                     <div className={this.props.PanelHeight() < 32 ? "audiobox-marker-minicontainer" : "audiobox-marker-container"} key={l[Id]} style={{ left: `${linkTime / NumCast(this.dataDoc.duration, 1) * 100}%` }}>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f74255933..853268d5b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -299,7 +299,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             } else if (this.Document.type === DocumentType.BUTTON) {
                 UndoManager.RunInBatch(() => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click");
             } else if (this.Document.isButton) {
-                DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey);
+                DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
             } else {
                 if (this.props.Document.isTemplateForField && !(e.ctrlKey || e.button > 0)) {
                     stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
@@ -316,17 +316,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     // follows a link - if the target is on screen, it highlights/pans to it.
     // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
     // depending on the followLinkLocation property of the source (or the link itself as a fallback);
-    followLinkClick = async (altKey: boolean, ctrlKey: boolean) => {
+    followLinkClick = async (altKey: boolean, ctrlKey: boolean, shiftKey: boolean) => {
         const batch = UndoManager.StartBatch("follow link click");
-        const docFollowLoc = StrCast(this.Document.followLinkLocation);
-        const { focus, addDocTab } = this.props;
-        await DocumentManager.Instance.FollowLink(undefined, this.props.Document,
-            // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards (except if 'inPlace', then leave alone)
-            (doc, followLoc, afterFocus) => focus(this.props.Document, true, 1, () => {
-                addDocTab(doc, docFollowLoc || followLoc) && focus(doc, true, undefined, afterFocus);
-                return (docFollowLoc || followLoc) === "inPlace" ? false : true;
-            }),
-            ctrlKey, altKey, this.props.ContainingCollectionDoc, batch.end);
+        // open up target if it's not already in view ... 
+        const createViewFunc = (doc: Doc, followLoc: string, finished: Opt<() => void>) => {
+            const targetFocusAfterDocFocus = () => {
+                const where = StrCast(this.Document.followLinkLocation) || followLoc;
+                const hackToCallFinishAfterFocus = () => {
+                    setTimeout(() => finished?.(), 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout.  
+                    return false; // we must return false here so that the zoom to the document is not reversed.  If it weren't for needing to call finished(), we wouldn't need this function at all since not having it is equivalent to returning false
+                }
+                this.props.addDocTab(doc, where) && this.props.focus(doc, true, undefined, hackToCallFinishAfterFocus); //  add the target and focus on it.
+                return where !== "inPlace"; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target)
+            };
+            // first focus & zoom onto this (the clicked document).  Then execute the function to focus on the target
+            this.props.focus(this.props.Document, true, 1, targetFocusAfterDocFocus);
+        };
+        await DocumentManager.Instance.FollowLink(undefined, this.props.Document, createViewFunc, shiftKey, this.props.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
     }
 
     handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
@@ -989,13 +995,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
     linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document);
 
-    // used to decide whether a link document should be created or not.
+    // used to decide whether a link anchor view should be created or not.
     // if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here.
     // would be good to generalize this some way.
     isNonTemporalLink = (linkDoc: Doc) => {
         const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
-        const ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1_timecode : linkDoc.anchor2_timecode;
-        return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true;
+        return anchor.type === DocumentType.AUDIO ? false : true;
     }
 
     @observable _link: Opt<Doc>;
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index babca2a14..20d734244 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -84,7 +84,7 @@ export class FormattedTextBoxComment {
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
                     DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
-                        (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"));
+                        (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
                     textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), "onRight");
                 }
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 5cb766c03..71b19f3a6 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -97,9 +97,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         else if (e.button === 0) {
             const annoGroup = await Cast(this.props.document.group, Doc);
             if (annoGroup) {
-                DocumentManager.Instance.FollowLink(undefined, annoGroup,
-                    (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : "onRight"),
-                    false, false, undefined);
+                DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false,  undefined);
                 e.stopPropagation();
             }
         }
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 480a55da0..8c719ccd8 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -12,7 +12,7 @@ function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
 }
 
-const tracing = true;
+const tracing = false;
 export function TraceMobx() {
     tracing && trace();
 }
-- 
cgit v1.2.3-70-g09d2


From 48478855cc28aa423b2d1e482bd181c8c5066da6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 5 Apr 2020 15:13:25 -0400
Subject: adding nativeWidth as props stuff

---
 src/client/util/RichTextSchema.tsx                 |  4 +-
 src/client/views/GestureOverlay.tsx                | 13 ++++---
 src/client/views/MainView.tsx                      | 10 ++++-
 src/client/views/OverlayView.tsx                   |  2 +
 src/client/views/Palette.tsx                       |  4 +-
 src/client/views/RecommendationsBox.tsx            |  4 +-
 src/client/views/TemplateMenu.tsx                  |  6 ++-
 .../views/collections/CollectionDockingView.tsx    |  4 +-
 .../views/collections/CollectionLinearView.tsx     |  6 ++-
 .../views/collections/CollectionSchemaCells.tsx    |  2 +
 .../views/collections/CollectionStackingView.tsx   | 32 +++++++++------
 src/client/views/collections/CollectionSubView.tsx |  8 ++--
 .../views/collections/CollectionTimeView.tsx       |  5 +--
 .../views/collections/CollectionTreeView.tsx       |  2 +
 src/client/views/collections/CollectionView.tsx    |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  5 ++-
 src/client/views/nodes/AudioBox.tsx                |  4 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx | 45 +++++++++++++---------
 .../views/nodes/ContentFittingDocumentView.tsx     | 25 +++++++-----
 src/client/views/nodes/DocumentView.tsx            | 14 +++++--
 src/client/views/nodes/FieldView.tsx               |  2 +
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +-
 src/client/views/nodes/ImageBox.tsx                |  4 +-
 src/client/views/nodes/KeyValuePair.tsx            |  6 ++-
 src/client/views/nodes/LinkBox.tsx                 |  2 +
 src/client/views/nodes/ScreenshotBox.tsx           |  4 +-
 src/client/views/nodes/VideoBox.tsx                |  4 +-
 src/client/views/nodes/WebBox.tsx                  |  4 +-
 src/client/views/pdf/PDFViewer.tsx                 |  4 +-
 src/mobile/MobileInterface.tsx                     | 14 +++++--
 src/new_fields/documentSchemas.ts                  |  2 +-
 src/new_fields/util.ts                             |  2 +-
 32 files changed, 170 insertions(+), 81 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index d4568b17f..de2707d36 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -16,7 +16,7 @@ import { listSpec } from "../../new_fields/Schema";
 import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
 import { ComputedField } from "../../new_fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs } from "../documents/Documents";
 import { CollectionViewType } from "../views/collections/CollectionView";
@@ -824,6 +824,8 @@ export class DashDocView {
                     addDocTab={this._textBox.props.addDocTab}
                     pinToPres={returnFalse}
                     renderDepth={self._textBox.props.renderDepth + 1}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     PanelWidth={finalLayout[WidthSym]}
                     PanelHeight={finalLayout[HeightSym]}
                     focus={this.outerFocus}
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 68f1d13fb..110b61efe 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -15,7 +15,7 @@ import { Scripting } from "../util/Scripting";
 import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types";
 import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
 import HorizontalPalette from "./Palette";
-import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, numberRange } from "../../Utils";
+import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, numberRange, returnZero } from "../../Utils";
 import { DocumentView } from "./nodes/DocumentView";
 import { Transform } from "../util/Transform";
 import { DocumentContentsView } from "./nodes/DocumentContentsView";
@@ -695,7 +695,8 @@ export default class GestureOverlay extends Touchable {
             </svg>]
         ];
     }
-
+    screenToLocalTransform = () => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1);
+    return300 = () => 300;
     @action
     public openFloatingDoc = (doc: Doc) => {
         this._clipboardDoc =
@@ -709,10 +710,12 @@ export default class GestureOverlay extends Touchable {
                 pinToPres={emptyFunction}
                 onClick={undefined}
                 removeDocument={undefined}
-                ScreenToLocalTransform={() => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1)}
+                ScreenToLocalTransform={this.screenToLocalTransform}
                 ContentScaling={returnOne}
-                PanelWidth={() => 300}
-                PanelHeight={() => 300}
+                PanelWidth={this.return300}
+                PanelHeight={this.return300}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
                 renderDepth={0}
                 backgroundColor={returnEmptyString}
                 focus={emptyFunction}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 420cc95ec..bef92f0fd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,7 +13,7 @@ import { listSpec } from '../../new_fields/Schema';
 import { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types';
 import { TraceMobx } from '../../new_fields/util';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, Utils } from '../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, returnTrue, Utils } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
@@ -307,6 +307,8 @@ export class MainView extends React.Component {
             removeDocument={undefined}
             ScreenToLocalTransform={Transform.Identity}
             ContentScaling={returnOne}
+            NativeHeight={returnZero}
+            NativeWidth={returnZero}
             PanelWidth={this.getPWidth}
             PanelHeight={this.getPHeight}
             renderDepth={0}
@@ -405,6 +407,8 @@ export class MainView extends React.Component {
                     onClick={undefined}
                     ScreenToLocalTransform={Transform.Identity}
                     ContentScaling={returnOne}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     PanelWidth={this.flyoutWidthFunc}
                     PanelHeight={this.getPHeight}
                     renderDepth={0}
@@ -424,6 +428,8 @@ export class MainView extends React.Component {
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     rootSelected={returnTrue}
                     removeDocument={returnFalse}
                     onClick={undefined}
@@ -524,6 +530,8 @@ export class MainView extends React.Component {
                     onClick={undefined}
                     ScreenToLocalTransform={this.buttonBarXf}
                     ContentScaling={returnOne}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     PanelWidth={this.flyoutWidthFunc}
                     PanelHeight={this.getContentsHeight}
                     renderDepth={0}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 7cfae0bbb..4000cade5 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -182,6 +182,8 @@ export class OverlayView extends React.Component {
                     addDocument={undefined}
                     removeDocument={undefined}
                     ContentScaling={returnOne}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     PanelWidth={returnOne}
                     PanelHeight={returnOne}
                     ScreenToLocalTransform={Transform.Identity}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index d088d5bba..63744cb50 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import * as React from "react";
 import { Doc } from "../../new_fields/Doc";
 import { NumCast } from "../../new_fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { DocumentView } from "./nodes/DocumentView";
 import "./Palette.scss";
@@ -49,6 +49,8 @@ export default class Palette extends React.Component<PaletteProps> {
                             onClick={undefined}
                             ScreenToLocalTransform={Transform.Identity}
                             ContentScaling={returnOne}
+                            NativeHeight={returnZero}
+                            NativeWidth={returnZero}
                             PanelWidth={() => window.screen.width}
                             PanelHeight={() => window.screen.height}
                             renderDepth={0}
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index ddeda28ac..e66fd3eb4 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -6,7 +6,7 @@ import "./RecommendationsBox.scss";
 import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
 import { DocumentIcon } from "./nodes/DocumentIcon";
 import { StrCast, NumCast } from "../../new_fields/Types";
-import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath } from "../../Utils";
+import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath, returnZero } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { ObjectField } from "../../new_fields/ObjectField";
 import { DocumentView } from "./nodes/DocumentView";
@@ -73,6 +73,8 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
                 addDocTab={returnFalse}
                 pinToPres={returnFalse}
                 renderDepth={1}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
                 PanelWidth={returnXDimension}
                 PanelHeight={returnYDimension}
                 focus={emptyFunction}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index f2fb26679..c72f3681a 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -10,7 +10,7 @@ import { Doc, DocListCast } from "../../new_fields/Doc";
 import { Docs, } from "../documents/Documents";
 import { StrCast, Cast } from "../../new_fields/Types";
 import { CollectionTreeView } from "./collections/CollectionTreeView";
-import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath } from "../../Utils";
+import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
 import { Scripting } from "../util/Scripting";
@@ -149,6 +149,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                     select={emptyFunction}
                     renderDepth={1}
                     addDocTab={returnFalse}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     PanelWidth={this.return100}
                     PanelHeight={this.return100}
                     treeViewHideHeaderFields={true}
@@ -161,7 +163,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             );
         }
         return <ul className="template-list" style={{ display: "block" }}>
-            <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress}/>
+            <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />
             {templateMenu}
         </ul>;
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 11eecf771..28aaf0c57 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -15,7 +15,7 @@ import { FieldId } from "../../../new_fields/RefField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from '../../../new_fields/util';
 import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { emptyFunction, returnOne, returnTrue, Utils } from "../../../Utils";
+import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -794,6 +794,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             ContentScaling={this.contentScaling}
             PanelWidth={this.panelWidth}
             PanelHeight={this.panelHeight}
+            NativeHeight={returnZero}
+            NativeWidth={returnZero}
             ScreenToLocalTransform={this.ScreenToLocalTransform}
             renderDepth={0}
             parentActive={returnTrue}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 3bd50ad52..cb0206260 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -4,7 +4,7 @@ import * as React from 'react';
 import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc';
 import { makeInterface } from '../../../new_fields/Schema';
 import { BoolCast, NumCast, StrCast, Cast, ScriptCast } from '../../../new_fields/Types';
-import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse } from '../../../Utils';
+import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse, returnZero } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { Transform } from '../../util/Transform';
 import "./CollectionLinearView.scss";
@@ -114,6 +114,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 onClick={undefined}
                                 ScreenToLocalTransform={this.getTransform(dref)}
                                 ContentScaling={returnOne}
+                                NativeHeight={returnZero}
+                                NativeWidth={returnZero}
                                 PanelWidth={nested ? pair.layout[WidthSym] : () => this.dimension()}// ugh - need to get rid of this inline function to avoid recomputing
                                 PanelHeight={nested ? pair.layout[HeightSym] : () => this.dimension()}
                                 renderDepth={this.props.renderDepth + 1}
@@ -123,7 +125,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                                 whenActiveChanged={emptyFunction}
                                 bringToFront={emptyFunction}
                                 ContainingCollectionView={undefined}
-                                ContainingCollectionDoc={undefined}/>
+                                ContainingCollectionDoc={undefined} />
                         </div>;
                     })}
                 </div>
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 7b16ff546..ae71c54f7 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -172,6 +172,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             whenActiveChanged: emptyFunction,
             PanelHeight: returnZero,
             PanelWidth: returnZero,
+            NativeHeight: returnZero,
+            NativeWidth: returnZero,
             addDocTab: this.props.addDocTab,
             pinToPres: this.props.pinToPres,
             ContentScaling: returnOne
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d21e17bbc..8ceeb66f1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../new_fields/Schema";
 import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
 import { TraceMobx } from "../../../new_fields/util";
-import { Utils, setupMoveUpEvents, emptyFunction } from "../../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction, returnZero, returnOne } from "../../../Utils";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -24,7 +24,7 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
-import { Docs } from "../../documents/Documents";
+import { DocumentView } from "../nodes/DocumentView";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -53,6 +53,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
 
     children(docs: Doc[], columns?: number) {
+        TraceMobx();
         this._docXfs.length = 0;
         return docs.map((d, i) => {
             const height = () => this.getDocHeight(d);
@@ -115,7 +116,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     componentDidMount() {
         super.componentDidMount();
         this._heightDisposer = reaction(() => {
-            if (this.props.Document._autoHeight) {
+            if (this.props.Document._autoHeight && !this.props.NativeHeight()) {
                 const sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
                 if (this.isStackingView) {
                     const res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => {
@@ -168,12 +169,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
         const layoutDoc = Doc.Layout(doc, this.props.childLayoutTemplate?.());
         const height = () => this.getDocHeight(doc);
-        return <ContentFittingDocumentView
+        return <DocumentView
             Document={doc}
-            DataDocument={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
+            DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
             backgroundColor={this.props.backgroundColor}
             LayoutDoc={this.props.childLayoutTemplate}
             LibraryPath={this.props.LibraryPath}
+            FreezeDimensions={this.props.freezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             fitToBox={this.props.fitToBox}
             rootSelected={this.rootSelected}
@@ -181,18 +183,22 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler}
             PanelWidth={width}
             PanelHeight={height}
-            getTransform={dxf}
+            ScreenToLocalTransform={dxf}
             focus={this.props.focus}
-            CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document}
-            CollectionView={this.props.CollectionView}
+            ContainingCollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document}
+            ContainingCollectionView={this.props.CollectionView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
-            active={this.props.active}
+            parentActive={this.props.active}
+            bringToFront={emptyFunction}
+            NativeHeight={returnZero}
+            NativeWidth={returnZero}
+            ContentScaling={returnOne}
             whenActiveChanged={this.props.whenActiveChanged}
             addDocTab={this.props.addDocTab}
             pinToPres={this.props.pinToPres}>
-        </ContentFittingDocumentView>;
+        </DocumentView>;
     }
 
     getDocWidth(d?: Doc) {
@@ -387,7 +393,11 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]));
     }
 
-    @computed get scaling() { return !this.props.Document._nativeWidth ? 1 : this.props.PanelHeight() / NumCast(this.props.Document._nativeHeight); }
+
+    @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth) || this.props.NativeWidth() || 0; }
+    @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight) || this.props.NativeHeight() || 0; }
+
+    @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; }
 
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d1d6ae3c1..32449de5e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -36,12 +36,15 @@ export interface CollectionViewProps extends FieldViewProps {
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
     rootSelected: () => boolean;
     fieldKey: string;
+    NativeWidth: () => number;
+    NativeHeight: () => number;
 }
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
     children?: never | (() => JSX.Element[]) | React.ReactNode;
-    overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explict list (see LinkBox)
+    freezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
+    overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
     ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox)
     isAnnotationOverlay?: boolean;
     annotationsKey: string;
@@ -213,9 +216,6 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this 
             if (docDragData) {
                 let added = false;
-                if (this.props.Document._freezeOnDrop) {
-                    de.complete.docDragData?.droppedDocuments.forEach(drop => Doc.freezeNativeDimensions(drop, drop[WidthSym](), drop[HeightSym]()));
-                }
                 if (docDragData.dropAction || docDragData.userDropAction) {
                     added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
                 } else if (docDragData.moveDocument) {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 4f77e8b0e..d9a10cdc8 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -5,7 +5,7 @@ import { List } from "../../../new_fields/List";
 import { ObjectField } from "../../../new_fields/ObjectField";
 import { RichTextField } from "../../../new_fields/RichTextField";
 import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast } from "../../../new_fields/Types";
+import { NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
 import { Scripting } from "../../util/Scripting";
 import { ContextMenu } from "../ContextMenu";
@@ -29,7 +29,6 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         this.props.Document.onChildClick = undefined;
     }
     componentDidMount() {
-        this.props.Document._freezeOnDrop = true;
         const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked;
         const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(containingCollection.childDetailed, alias, 'layout_detailView'); alias.layoutKey='layout_detailedView'; alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", containingCollection: Doc.name, shiftKey: "boolean" });
@@ -73,7 +72,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
 
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }}>
-            <CollectionFreeFormView  {...this.props} layoutEngine={this.layoutEngine} />
+            <CollectionFreeFormView {...this.props} freezeDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
         </div>;
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 77dd9a162..10da50e35 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -465,6 +465,8 @@ class TreeView extends React.Component<TreeViewProps> {
                         ContentScaling={returnOne}
                         PanelWidth={returnZero}
                         PanelHeight={returnZero}
+                        NativeHeight={returnZero}
+                        NativeWidth={returnZero}
                         renderDepth={1}
                         focus={emptyFunction}
                         parentActive={returnTrue}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a727da267..5d08a2bd8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,7 +13,7 @@ import { List } from '../../../new_fields/List';
 import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
 import { ImageField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
-import { Utils, setupMoveUpEvents, returnFalse } from '../../../Utils';
+import { Utils, setupMoveUpEvents, returnFalse, returnZero } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
@@ -424,6 +424,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
                     <CollectionTreeView {...this.props}
                         CollectionView={this}
                         treeViewHideTitle={true}
+                        NativeHeight={returnZero}
+                        NativeWidth={returnZero}
                         treeViewHideHeaderFields={true}
                         onCheckedClick={this.scriptField!}
                         ignoreFields={this.treeIgnoreFields}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d4e44b9a9..f22f1a304 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -89,8 +89,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
     @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; }
     @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
     @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc.xPadding, 10), NumCast(this.layoutDoc.yPadding, 10)); }
-    @computed get nativeWidth() { return this.Document._fitToContent ? 0 : NumCast(this.Document._nativeWidth); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : NumCast(this.Document._nativeHeight); }
+    @computed get nativeWidth() { return this.Document._fitToContent ? 0 : NumCast(this.Document._nativeWidth, this.props.NativeWidth()); }
+    @computed get nativeHeight() { return this.fitToContent ? 0 : NumCast(this.Document._nativeHeight, this.props.NativeHeight()); }
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
     private easing = () => this.props.Document.panTransformType === "Ease";
@@ -825,6 +825,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             DataDoc: childData,
             Document: childLayout,
             LibraryPath: this.libraryPath,
+            FreezeDimensions: this.props.freezeDimensions,
             layoutKey: undefined,
             rootSelected: this.rootSelected,
             dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index be0e1aec4..ff9630273 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -7,7 +7,7 @@ import { AudioField, nullAudio } from "../../../new_fields/URLField";
 import { DocExtendableComponent } from "../DocComponent";
 import { makeInterface, createSchema } from "../../../new_fields/Schema";
 import { documentSchema } from "../../../new_fields/documentSchemas";
-import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse } from "../../../Utils";
+import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse, returnZero } from "../../../Utils";
 import { runInAction, observable, reaction, IReactionDisposer, computed, action } from "mobx";
 import { DateField } from "../../../new_fields/DateField";
 import { SelectionManager } from "../../util/SelectionManager";
@@ -261,6 +261,8 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                                         <div className={this.props.PanelHeight() < 32 ? "audioBox-linker-mini" : "audioBox-linker"} key={"linker" + i}>
                                             <DocumentView {...this.props}
                                                 Document={l}
+                                                NativeHeight={returnZero}
+                                                NativeWidth={returnZero}
                                                 rootSelected={returnFalse}
                                                 layoutKey={Doc.LinkEndpoint(l, la2)}
                                                 ContainingCollectionDoc={this.props.Document}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 356192797..8a4842427 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -41,10 +41,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         const hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
         return (hgt === undefined && this.nativeWidth && this.nativeHeight) ? this.width * this.nativeHeight / this.nativeWidth : hgt;
     }
+    @computed get freezeDimensions() { return this.props.FreezeDimensions || this.layoutDoc._freezeChildDimensions; }
     @computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document) ? this.props.dataProvider(this.props.Document) : undefined; }
-    @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth); }
-    @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight); }
-
+    @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
+    @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
+   
     @computed get renderScriptDim() {
         if (this.Document.renderScript) {
             const someView = Cast(this.props.Document.someView, Doc);
@@ -68,6 +69,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         .scale(1 / this.contentScaling())
 
     focusDoc = (doc: Doc) => this.props.focus(doc, false);
+    NativeWidth = () => this.nativeWidth;
+    NativeHeight = () => this.nativeHeight;
     render() {
         TraceMobx();
         return <div className="collectionFreeFormDocumentView-container"
@@ -89,22 +92,26 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 pointerEvents: this.props.Document.isBackground ? "none" : undefined
             }} >
 
-            {!this.props.fitToBox ? 
-            <DocumentView {...this.props}
-                dragDivName={"collectionFreeFormDocumentView-container"}
-                ContentScaling={this.contentScaling}
-                ScreenToLocalTransform={this.getTransform}
-                backgroundColor={this.props.backgroundColor}
-                PanelWidth={this.panelWidth}
-                PanelHeight={this.panelHeight} /> 
-            : <ContentFittingDocumentView {...this.props}
-                CollectionDoc={this.props.ContainingCollectionDoc}
-                DataDocument={this.props.DataDoc}
-                getTransform={this.getTransform}
-                active={returnFalse}
-                focus={this.focusDoc}
-                PanelWidth={this.panelWidth}
-                PanelHeight={this.panelHeight}
+            {!this.props.fitToBox ?
+                <DocumentView {...this.props}
+                    dragDivName={"collectionFreeFormDocumentView-container"}
+                    ContentScaling={this.contentScaling}
+                    ScreenToLocalTransform={this.getTransform}
+                    backgroundColor={this.props.backgroundColor}
+                    NativeHeight={this.NativeHeight}
+                    NativeWidth={this.NativeWidth}
+                    PanelWidth={this.panelWidth}
+                    PanelHeight={this.panelHeight} />
+                : <ContentFittingDocumentView {...this.props}
+                    CollectionDoc={this.props.ContainingCollectionDoc}
+                    DataDocument={this.props.DataDoc}
+                    getTransform={this.getTransform}
+                    NativeHeight={this.NativeHeight}
+                    NativeWidth={this.NativeWidth}
+                    active={returnFalse}
+                    focus={this.focusDoc}
+                    PanelWidth={this.panelWidth}
+                    PanelHeight={this.panelHeight}
                 />}
         </div>;
     }
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 9ab6826a3..09675bf73 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -18,6 +18,9 @@ interface ContentFittingDocumentViewProps {
     Document?: Doc;
     DataDocument?: Doc;
     LayoutDoc?: () => Opt<Doc>;
+    NativeWidth?: () => number;
+    NativeHeight?: () => number;
+    FreezeDimensions?: boolean;
     LibraryPath: Doc[];
     childDocs?: Doc[];
     renderDepth: number;
@@ -47,12 +50,12 @@ interface ContentFittingDocumentViewProps {
 export class ContentFittingDocumentView extends React.Component<ContentFittingDocumentViewProps>{
     public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
     private get layoutDoc() { return this.props.Document && (this.props.LayoutDoc?.() || Doc.Layout(this.props.Document)); }
-    private get nativeWidth() { return NumCast(this.layoutDoc?._nativeWidth, this.props.PanelWidth()); }
-    private get nativeHeight() { return NumCast(this.layoutDoc?._nativeHeight, this.props.PanelHeight()); }
+    private nativeWidth = () => NumCast(this.layoutDoc?._nativeWidth, this.props.NativeWidth?.() || this.props.PanelWidth());
+    private nativeHeight = () => NumCast(this.layoutDoc?._nativeHeight, this.props.NativeHeight?.() || this.props.PanelHeight());
     @computed get scaling() {
-        const wscale = this.props.PanelWidth() / (this.nativeWidth || this.props.PanelWidth() || 1);
-        if (wscale * this.nativeHeight > this.props.PanelHeight()) {
-            return (this.props.PanelHeight() / (this.nativeHeight || this.props.PanelHeight() || 1)) || 1;
+        const wscale = this.props.PanelWidth() / (this.nativeWidth() || this.props.PanelWidth() || 1);
+        if (wscale * this.nativeHeight() > this.props.PanelHeight()) {
+            return (this.props.PanelHeight() / (this.nativeHeight() || this.props.PanelHeight() || 1)) || 1;
         }
         return wscale || 1;
     }
@@ -61,12 +64,12 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
     private PanelWidth = () => this.panelWidth;
     private PanelHeight = () => this.panelHeight;
 
-    @computed get panelWidth() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); }
-    @computed get panelHeight() { return this.nativeHeight && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); }
+    @computed get panelWidth() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
+    @computed get panelHeight() { return this.nativeHeight && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
 
     private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
-    private get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; }
-    private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight * this.contentScaling()) / 2 : 0; }
+    private get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+    private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
 
     @computed get borderRounding() { return StrCast(this.props.Document?.borderRounding); }
 
@@ -81,7 +84,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                     style={{
                         transform: `translate(${this.centeringOffset}px, 0px)`,
                         borderRadius: this.borderRounding,
-                        height: Math.abs(this.centeringYOffset) > 0.001 ? `${100 * this.nativeHeight / this.nativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
+                        height: Math.abs(this.centeringYOffset) > 0.001 ? `${100 * this.nativeHeight() / this.nativeWidth() * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
                         width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth()
                     }}>
                     <DocumentView {...this.props}
@@ -89,6 +92,8 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
                         DataDoc={this.props.DataDocument}
                         LayoutDoc={this.props.LayoutDoc}
                         LibraryPath={this.props.LibraryPath}
+                        NativeWidth={this.nativeWidth}
+                        NativeHeight={this.nativeHeight}
                         fitToBox={this.props.fitToBox}
                         layoutKey={this.props.layoutKey}
                         dropAction={this.props.dropAction}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index aa3c1b3a2..9c9f2e48e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -3,7 +3,7 @@ import * as fa from '@fortawesome/free-solid-svg-icons';
 import { action, computed, runInAction, trace, observable } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from "request-promise";
-import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
 import { Document, PositionDocument } from '../../../new_fields/documentSchemas';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { InkTool } from '../../../new_fields/InkField';
@@ -56,6 +56,9 @@ export type DocFocusFunc = () => boolean;
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    FreezeDimensions?: boolean;
+    NativeWidth: () => number;
+    NativeHeight: () => number;
     Document: Doc;
     DataDoc?: Doc;
     LayoutDoc?: () => Opt<Doc>;
@@ -109,11 +112,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
     @computed get topMost() { return this.props.renderDepth === 0; }
-    @computed get nativeWidth() { return this.layoutDoc._nativeWidth || 0; }
-    @computed get nativeHeight() { return this.layoutDoc._nativeHeight || 0; }
+    @computed get freezeDimensions() { return this.props.FreezeDimensions || this.layoutDoc._freezeChildDimensions; }
+    @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
+    @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
     @computed get onClickHandler() { return this.props.onClick || this.layoutDoc.onClick || this.Document.onClick; }
     @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; }
     @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; }
+    NativeWidth = () => this.nativeWidth;
+    NativeHeight = () => this.nativeHeight;
 
     private _firstX: number = -1;
     private _firstY: number = -1;
@@ -965,6 +971,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         TraceMobx();
         return (<DocumentContentsView ContainingCollectionView={this.props.ContainingCollectionView}
             ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+            NativeHeight={this.NativeHeight}
+            NativeWidth={this.NativeWidth}
             Document={this.props.Document}
             DataDoc={this.props.DataDoc}
             LayoutDoc={this.props.LayoutDoc}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 13a1becf7..dfcb03985 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -45,6 +45,8 @@ export interface FieldViewProps {
     focus: (doc: Doc) => void;
     PanelWidth: () => number;
     PanelHeight: () => number;
+    NativeHeight: () => number;
+    NativeWidth: () => number;
     setVideoBox?: (player: VideoBox) => void;
     ContentScaling: () => number;
     ChromeHeight?: () => number;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 94195fbd6..37770a2e1 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -22,7 +22,7 @@ import { RichTextUtils } from '../../../new_fields/RichTextUtils';
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
 import { Cast, NumCast, StrCast, BoolCast, DateCast } from "../../../new_fields/Types";
 import { TraceMobx } from '../../../new_fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, Utils, returnTrue } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, Utils, returnTrue, returnZero } from '../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -1216,6 +1216,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                         <CollectionFreeFormView {...this.props}
                             PanelHeight={this.props.PanelHeight}
                             PanelWidth={this.sidebarWidth}
+                            NativeHeight={returnZero}
+                            NativeWidth={returnZero}
                             annotationsKey={this.annotationKey}
                             isAnnotationOverlay={false}
                             focus={this.props.focus}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 00057055f..a4d3f3d79 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -14,7 +14,7 @@ import { ComputedField } from '../../../new_fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
 import { AudioField, ImageField } from '../../../new_fields/URLField';
 import { TraceMobx } from '../../../new_fields/util';
-import { emptyFunction, returnOne, Utils } from '../../../Utils';
+import { emptyFunction, returnOne, Utils, returnZero } from '../../../Utils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
 import { Docs } from '../../documents/Documents';
 import { Networking } from '../../Network';
@@ -445,6 +445,8 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
             <CollectionFreeFormView {...this.props}
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
                 annotationsKey={this.annotationKey}
                 isAnnotationOverlay={true}
                 focus={this.props.focus}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index ef039c857..6dc4ae578 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -62,13 +62,15 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             rootSelected: returnFalse,
             isSelected: returnFalse,
             select: emptyFunction,
-            dropAction:"alias",
-            bringToFront:emptyFunction,
+            dropAction: "alias",
+            bringToFront: emptyFunction,
             renderDepth: 1,
             active: returnFalse,
             whenActiveChanged: emptyFunction,
             ScreenToLocalTransform: Transform.Identity,
             focus: emptyFunction,
+            NativeHeight: returnZero,
+            NativeWidth: returnZero,
             PanelWidth: this.props.PanelWidth,
             PanelHeight: this.props.PanelHeight,
             addDocTab: returnFalse,
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 0e327e130..542c86049 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -23,6 +23,8 @@ export class LinkBox extends DocExtendableComponent<FieldViewProps, LinkDocument
             <CollectionTreeView {...this.props}
                 ChromeHeight={returnZero}
                 overrideDocuments={[this.dataDoc]}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
                 ignoreFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
                 annotationsKey={""}
                 CollectionView={undefined}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 7c58a5148..58ff4971a 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -10,7 +10,7 @@ import { makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { Cast, StrCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
-import { emptyFunction, returnFalse, returnOne, Utils } from "../../../Utils";
+import { emptyFunction, returnFalse, returnOne, Utils, returnZero } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { ContextMenu } from "../ContextMenu";
@@ -170,6 +170,8 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
                 <CollectionFreeFormView {...this.props}
                     PanelHeight={this.props.PanelHeight}
                     PanelWidth={this.props.PanelWidth}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     annotationsKey={this.annotationKey}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 24b66d8f7..d384ad12f 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -11,7 +11,7 @@ import { createSchema, makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
-import { Utils, emptyFunction, returnOne } from "../../../Utils";
+import { Utils, emptyFunction, returnOne, returnZero } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { ContextMenu } from "../ContextMenu";
@@ -352,6 +352,8 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
                 <CollectionFreeFormView {...this.props}
                     PanelHeight={this.props.PanelHeight}
                     PanelWidth={this.props.PanelWidth}
+                    NativeHeight={returnZero}
+                    NativeWidth={returnZero}
                     annotationsKey={this.annotationKey}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 838fbefb1..ea5d601ec 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -9,7 +9,7 @@ import { InkTool } from "../../../new_fields/InkField";
 import { makeInterface } from "../../../new_fields/Schema";
 import { Cast, NumCast } from "../../../new_fields/Types";
 import { WebField } from "../../../new_fields/URLField";
-import { Utils, returnOne, emptyFunction } from "../../../Utils";
+import { Utils, returnOne, emptyFunction, returnZero } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
@@ -335,6 +335,8 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
                 PanelHeight={this.props.PanelHeight}
                 PanelWidth={this.props.PanelWidth}
                 annotationsKey={this.annotationKey}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
                 focus={this.props.focus}
                 isSelected={this.props.isSelected}
                 isAnnotationOverlay={true}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 25ceb6218..36e687f71 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List";
 import { makeInterface, createSchema } from "../../../new_fields/Schema";
 import { ScriptField, ComputedField } from "../../../new_fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules } from "../../../Utils";
+import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, returnZero } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { CompiledScript, CompileScript } from "../../util/Scripting";
@@ -648,6 +648,8 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
                 setPreviewCursor={this.setPreviewCursor}
                 PanelHeight={this.panelWidth}
                 PanelWidth={this.panelHeight}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
                 dropAction={"alias"}
                 VisibleHeight={this.visibleHeight}
                 focus={this.props.focus}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 5f766c121..c87ac719c 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -7,7 +7,7 @@ import { Doc, DocListCast } from '../new_fields/Doc';
 import { Docs } from '../client/documents/Documents';
 import { CollectionView } from '../client/views/collections/CollectionView';
 import { DocumentView } from '../client/views/nodes/DocumentView';
-import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from '../Utils';
+import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, returnZero } from '../Utils';
 import { Transform } from '../client/util/Transform';
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faPenNib, faHighlighter, faEraser, faMousePointer, faBreadSlice, faTrash, faCheck, faLongArrowAltLeft } from '@fortawesome/free-solid-svg-icons';
@@ -129,6 +129,8 @@ export default class MobileInterface extends React.Component {
                 onClick={undefined}
                 ScreenToLocalTransform={Transform.Identity}
                 ContentScaling={returnOne}
+                NativeHeight={returnZero}
+                NativeWidth={returnZero}
                 PanelWidth={() => window.screen.width}
                 PanelHeight={() => window.screen.height}
                 renderDepth={0}
@@ -173,6 +175,8 @@ export default class MobileInterface extends React.Component {
         e.stopPropagation();
     }
 
+    panelHeight = () => window.innerHeight;
+    panelWidth = () => window.innerWidth;
     renderInkingContent = () => {
         console.log("rendering inking content");
         // TODO: support panning and zooming
@@ -201,8 +205,10 @@ export default class MobileInterface extends React.Component {
                         bringToFront={emptyFunction}
                         addDocTab={returnFalse}
                         pinToPres={emptyFunction}
-                        PanelHeight={() => window.innerHeight}
-                        PanelWidth={() => window.innerWidth}
+                        PanelWidth={this.panelWidth}
+                        PanelHeight={this.panelHeight}
+                        NativeHeight={returnZero}
+                        NativeWidth={returnZero}
                         focus={emptyFunction}
                         isSelected={returnFalse}
                         select={emptyFunction}
@@ -289,6 +295,8 @@ export default class MobileInterface extends React.Component {
                         onClick={undefined}
                         ScreenToLocalTransform={Transform.Identity}
                         ContentScaling={returnOne}
+                        NativeHeight={returnZero}
+                        NativeWidth={returnZero}
                         PanelWidth={() => window.screen.width}
                         PanelHeight={() => window.screen.height}
                         renderDepth={0}
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 1a5a471a0..ff02596f7 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -28,7 +28,7 @@ export const documentSchema = createSchema({
     _pivotField: "string",      // specifies which field should be used as the timeline/pivot axis
     _replacedChrome: "string",  // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's.
     _chromeStatus: "string",    // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed'
-    _freezeOnDrop: "boolean",    // whether a document without native dimensions should have its width/height frozen as native dimensions on drop.  Used by Timeline view to make sure documents are scaled to fit the display thumbnail
+    _freezeChildDimensions: "boolean", // freezes child document dimensions (e.g., used by time/pivot view to make sure all children will be scaled to fit their display rectangle)
     color: "string",            // foreground color of document
     backgroundColor: "string",  // background color of document
     opacity: "number",          // opacity of document
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 8c719ccd8..480a55da0 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -12,7 +12,7 @@ function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
 }
 
-const tracing = false;
+const tracing = true;
 export function TraceMobx() {
     tracing && trace();
 }
-- 
cgit v1.2.3-70-g09d2


From ae64711a1bcfc35c9ca0c86d99c99962642c9b09 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 9 Apr 2020 18:27:51 -0400
Subject: fixed links to have a timecode again. updated a bunch of
 this.Document calls to this.layoutDoc, this.dataDoc, etc

---
 src/client/documents/Documents.ts                  |  4 ++-
 src/client/util/DocumentManager.ts                 |  5 ++-
 src/client/views/DocComponent.tsx                  | 12 +++++--
 src/client/views/InkingStroke.tsx                  | 12 +++----
 src/client/views/nodes/AudioBox.tsx                | 33 ++++++++++---------
 src/client/views/nodes/DocumentView.tsx            |  9 +++---
 src/client/views/nodes/ImageBox.tsx                | 29 +++++++++--------
 src/client/views/nodes/ScreenshotBox.tsx           | 37 +++++++++++-----------
 src/client/views/nodes/VideoBox.tsx                |  7 ++--
 .../views/presentationview/PresElementBox.tsx      |  2 +-
 10 files changed, 82 insertions(+), 68 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 88179afd9..c823c3233 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -511,7 +511,7 @@ export namespace Docs {
 
         export function ScriptingDocument(script: Opt<ScriptField>, options: DocumentOptions = {}, fieldKey?: string) {
             const res = InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script, options);
-            fieldKey && res.proto instanceof Doc && ((res.proto as Doc).layout = ScriptingBox.LayoutString(fieldKey));
+            fieldKey && res.proto instanceof Doc && (res.proto.layout = ScriptingBox.LayoutString(fieldKey));
             return res;
         }
 
@@ -554,6 +554,8 @@ export namespace Docs {
             const linkDocProto = Doc.GetProto(doc);
             linkDocProto.anchor1 = source.doc;
             linkDocProto.anchor2 = target.doc;
+            linkDocProto.anchor1_timecode = source.doc.currentTimecode || source.doc.displayTimecode;
+            linkDocProto.anchor2_timecode = target.doc.currentTimecode || target.doc.displayTimecode;
 
             if (linkDocProto.layout_key1 === undefined) {
                 Cast(linkDocProto.proto, Doc, null).layout_key1 = LinkAnchorBox.LayoutString("anchor1");
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 3d5306841..fc2f251a5 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -219,9 +219,12 @@ export class DocumentManager {
         if (linkDoc) {
             const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
                 (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
+            const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") : 
+                                    doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number"):
+                (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number"):Cast(linkDoc.anchor1_timecode, "number")));
             if (target) {
                 const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
-                containerDoc.currentTimecode !== undefined && (containerDoc.currentTimecode = NumCast(target?.timecode));
+                containerDoc.currentTimecode = targetTimecode;
                 const targetContext = await target?.context as Doc;
                 const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
                 DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc[Id], undefined, doc, finished);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 325f7c042..d08065820 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -5,7 +5,6 @@ import { Cast } from '../../new_fields/Types';
 import { listSpec } from '../../new_fields/Schema';
 import { InkingControl } from './InkingControl';
 import { InkTool } from '../../new_fields/InkField';
-import { PositionDocument } from '../../new_fields/documentSchemas';
 import { InteractionUtils } from '../util/InteractionUtils';
 
 
@@ -22,6 +21,9 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D
         @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
+        // This is the data part of a document -- ie, the data that is constant across all views of the document
+        @computed get dataDoc() { return this.props.Document[DataSym]; }
+
         protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
     return Component;
@@ -39,7 +41,8 @@ interface DocExtendableProps {
 export function DocExtendableComponent<P extends DocExtendableProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
-        @computed get Document(): T { return schemaCtor(this.props.Document); }
+        //@computed get Document(): T { return schemaCtor(this.props.Document); }
+
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
         @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
@@ -47,6 +50,9 @@ export function DocExtendableComponent<P extends DocExtendableProps, T>(schemaCt
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
 
+        // key where data is stored
+        @computed get fieldKey() { return this.props.fieldKey; }
+
         active = (outsideReaction?: boolean) => !this.props.Document.isBackground && ((this.props.Document.forceActive && this.props.rootSelected(outsideReaction)) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0);//  && !InkingControl.Instance.selectedTool;  // bcz: inking state shouldn't affect static tools 
         protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
@@ -79,7 +85,7 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schema
         @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
 
         // key where data is stored
-        @computed get fieldKey() {  return this.props.fieldKey;  }
+        @computed get fieldKey() { return this.props.fieldKey; }
 
         protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index a791eed40..db5e2912d 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import { documentSchema } from "../../new_fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../new_fields/InkField";
 import { makeInterface } from "../../new_fields/Schema";
-import { Cast } from "../../new_fields/Types";
+import { Cast, StrCast } from "../../new_fields/Types";
 import { DocExtendableComponent } from "./DocComponent";
 import { InkingControl } from "./InkingControl";
 import "./InkingStroke.scss";
@@ -29,20 +29,20 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
     @computed get PanelHeight() { return this.props.PanelHeight(); }
 
     private analyzeStrokes = () => {
-        const data: InkData = Cast(this.Document.data, InkField) ?.inkData ?? [];
-        CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.Document, ["inkAnalysis", "handwriting"], [data]);
+        const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField) ?.inkData ?? [];
+        CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]);
     }
 
     render() {
         TraceMobx();
-        const data: InkData = Cast(this.Document.data, InkField) ?.inkData ?? [];
+        const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField) ?.inkData ?? [];
         const xs = data.map(p => p.X);
         const ys = data.map(p => p.Y);
         const left = Math.min(...xs);
         const top = Math.min(...ys);
         const right = Math.max(...xs);
         const bottom = Math.max(...ys);
-        const points = InteractionUtils.CreatePolyline(data, left, top, this.Document.color ?? InkingControl.Instance.selectedColor, this.Document.strokeWidth ?? parseInt(InkingControl.Instance.selectedWidth));
+        const points = InteractionUtils.CreatePolyline(data, left, top, StrCast(this.layoutDoc.color, InkingControl.Instance.selectedColor), this.Document.strokeWidth ?? parseInt(InkingControl.Instance.selectedWidth));
         const width = right - left;
         const height = bottom - top;
         const scaleX = this.PanelWidth / width;
@@ -54,7 +54,7 @@ export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocu
                 style={{
                     transformOrigin: "top left",
                     transform: `scale(${scaleX}, ${scaleY})`,
-                    mixBlendMode: this.Document.tool === InkTool.Highlighter ? "multiply" : "unset",
+                    mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset",
                     pointerEvents: "all"
                 }}
                 onContextMenu={() => {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index ff9630273..9ac41a528 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -71,7 +71,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             scrollLinkId => {
                 if (scrollLinkId) {
                     DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
-                        const linkTime = Doc.AreProtosEqual(l.anchor1 as Doc, this.dataDoc) ? NumCast((l.anchor1 as Doc).timecode) : NumCast((l.anchor2 as Doc).timecode);
+                        const linkTime = Doc.AreProtosEqual(l.anchor1 as Doc, this.dataDoc) ? NumCast(l.anchor1_timecode) : NumCast(l.anchor2_timecode);
                         setTimeout(() => { this.playFromTime(linkTime); Doc.linkFollowHighlight(l); }, 250);
                     });
                     Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
@@ -80,10 +80,10 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
             selected => {
                 const sel = selected.length ? selected[0].props.Document : undefined;
-                this.Document.playOnSelect && this.recordingStart && sel && sel.creationDate && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime());
-                this.Document.playOnSelect && this.recordingStart && !sel && this.pause();
+                this.layoutDoc.playOnSelect && this.recordingStart && sel && sel.creationDate && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime());
+                this.layoutDoc.playOnSelect && this.recordingStart && !sel && this.pause();
             });
-        this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, (time) => this.Document.playOnSelect && this.playFromTime(AudioBox._scrubTime));
+        this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime));
     }
 
     timecodeChanged = () => {
@@ -92,17 +92,16 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
             DocListCast(this.dataDoc.links).map(l => {
                 let la1 = l.anchor1 as Doc;
-                const la2 = l.anchor2 as Doc;
-                let linkTime = NumCast(la2.timecode);
+                let linkTime = NumCast(l.anchor2_timecode);
                 if (Doc.AreProtosEqual(la1, this.dataDoc)) {
-                    linkTime = NumCast(la1.timecode);
+                    linkTime = NumCast(l.anchor1_timecode);
                     la1 = l.anchor2 as Doc;
                 }
-                if (linkTime > NumCast(this.Document.currentTimecode) && linkTime < htmlEle.currentTime) {
+                if (linkTime > NumCast(this.layoutDoc.currentTimecode) && linkTime < htmlEle.currentTime) {
                     Doc.linkFollowHighlight(la1);
                 }
             });
-            this.Document.currentTimecode = htmlEle.currentTime;
+            this.layoutDoc.currentTimecode = htmlEle.currentTime;
         }
     }
 
@@ -136,7 +135,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
     updateRecordTime = () => {
         if (this.audioState === "recording") {
             setTimeout(this.updateRecordTime, 30);
-            this.Document.currentTimecode = (new Date().getTime() - this._recordStart) / 1000;
+            this.layoutDoc.currentTimecode = (new Date().getTime() - this._recordStart) / 1000;
         }
     }
 
@@ -158,7 +157,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: (this.Document.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.Document.playOnSelect = !this.Document.playOnSelect, icon: "expand-arrows-alt" });
+        funcs.push({ description: (this.layoutDoc.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" });
 
         ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" });
     }
@@ -185,7 +184,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
         e.stopPropagation();
     }
     onStop = (e: any) => {
-        this.Document.playOnSelect = !this.Document.playOnSelect;
+        this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect;
         e.stopPropagation();
     }
     onFile = (e: any) => {
@@ -227,7 +226,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             {!this.path ?
                 <div className="audiobox-buttons">
                     <div className="audiobox-dictation" onClick={this.onFile}>
-                        <FontAwesomeIcon style={{ width: "30px", background: this.Document.playOnSelect ? "yellow" : "dimGray" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+                        <FontAwesomeIcon style={{ width: "30px", background: this.layoutDoc.playOnSelect ? "yellow" : "dimGray" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
                     </div>
                     <button className={`audiobox-record${interactive}`} style={{ backgroundColor: this.audioState === "recording" ? "red" : "black" }}>
                         {this.audioState === "recording" ? "STOP" : "RECORD"}
@@ -242,7 +241,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                                 if (e.button === 0 && !e.ctrlKey) {
                                     const rect = (e.target as any).getBoundingClientRect();
                                     const wasPaused = this.audioState === "paused";
-                                    this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
+                                    this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
                                     wasPaused && this.pause();
                                     e.stopPropagation();
                                 }
@@ -250,11 +249,11 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                             {DocListCast(this.dataDoc.links).map((l, i) => {
                                 let la1 = l.anchor1 as Doc;
                                 let la2 = l.anchor2 as Doc;
-                                let linkTime = NumCast(la2.timecode);
+                                let linkTime = NumCast(l.anchor2_timecode);
                                 if (Doc.AreProtosEqual(la1, this.dataDoc)) {
                                     la1 = l.anchor2 as Doc;
                                     la2 = l.anchor1 as Doc;
-                                    linkTime = NumCast(la1.timecode);
+                                    linkTime = NumCast(l.anchor1_timecode);
                                 }
                                 return !linkTime ? (null) :
                                     <div className={this.props.PanelHeight() < 32 ? "audiobox-marker-minicontainer" : "audiobox-marker-container"} key={l[Id]} style={{ left: `${linkTime / NumCast(this.dataDoc.duration, 1) * 100}%` }}>
@@ -274,7 +273,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                                             onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { const wasPaused = this.audioState === "paused"; this.playFrom(linkTime); wasPaused && this.pause(); e.stopPropagation(); } }} />
                                     </div>;
                             })}
-                            <div className="audiobox-current" style={{ left: `${NumCast(this.Document.currentTimecode) / NumCast(this.dataDoc.duration, 1) * 100}%` }} />
+                            <div className="audiobox-current" style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / NumCast(this.dataDoc.duration, 1) * 100}%` }} />
                             {this.audio}
                         </div>
                     </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f0290f887..1bddac61c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -115,7 +115,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get freezeDimensions() { return this.props.FreezeDimensions; }
     @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
     @computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
-    @computed get onClickHandler() { return this.props.onClick || this.layoutDoc.onClick || this.Document.onClick; }
+    @computed get onClickHandler() { return this.props.onClick || Cast(this.layoutDoc.onClick, ScriptField, null) || this.Document.onClick; }
     @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; }
     @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; }
     NativeWidth = () => this.nativeWidth;
@@ -296,7 +296,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 Doc.UnBrushDoc(this.props.Document);
             } else if (this.onClickHandler?.script) {
                 SelectionManager.DeselectAll();
-                const func = () => this.onClickHandler!.script.run({
+                const func = () => this.onClickHandler.script.run({
                     this: this.props.Document,
                     self: this.rootDoc,
                     thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
@@ -643,7 +643,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     makeIntoPortal = async () => {
         const portalLink = DocListCast(this.Document.links).find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
-            const portal = Docs.Create.FreeformDocument([], { _width: (this.layoutDoc._width || 0) + 10, _height: this.layoutDoc._height || 0, title: StrCast(this.props.Document.title) + ".portal" });
+            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), title: StrCast(this.props.Document.title) + ".portal" });
             DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to");
         }
         this.Document.isLinkButton = true;
@@ -1017,7 +1017,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     // would be good to generalize this some way.
     isNonTemporalLink = (linkDoc: Doc) => {
         const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
-        return anchor.type === DocumentType.AUDIO ? false : true;
+        const ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1_timecode : linkDoc.anchor2_timecode;
+        return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true;
     }
 
     @observable _link: Opt<Doc>;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 651171294..55983ab77 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -239,12 +239,13 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     }
     @action onError = (error: any) => {
         const timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
-        if (timeout < 10) {
-            // setTimeout(this.retryPath, 500);
-        }
-        const original = StrCast(this.dataDoc.originalUrl);
-        if (error.type === "error" && original) {
-            this.dataDoc[this.fieldKey] = new ImageField(original);
+        if (timeout < 5) {
+            setTimeout(this.retryPath, 500);
+        } else {
+            const original = StrCast(this.dataDoc[this.fieldKey + "-originalUrl"]);
+            if (error.type === "error" && original) {
+                this.dataDoc[this.fieldKey] = new ImageField(original);
+            }
         }
     }
     _curSuffix = "_m";
@@ -341,7 +342,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
                     const { success, failure, idle, loading } = uploadIcons;
                     runInAction(() => this.uploadIcon = loading);
                     const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] });
-                    dataDoc.originalUrl = primary;
+                    dataDoc[this.props.fieldKey + "-originalUrl"] = primary;
                     let succeeded = true;
                     let data: ImageField | undefined;
                     try {
@@ -368,11 +369,11 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
         return { nativeWidth, nativeHeight };
     }
 
-        // this._curSuffix = "";
-        // if (w > 20) {
-        // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
-        // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
-        // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
+    // this._curSuffix = "";
+    // if (w > 20) {
+    // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
+    // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
+    // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
     @computed get paths() {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
         const alts = DocListCast(this.dataDoc[this.fieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
@@ -438,8 +439,8 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
         return (<div className={`imageBox${dragging}`} onContextMenu={this.specificContextMenu}
             style={{
                 transform: this.props.PanelWidth() ? undefined : `scale(${this.props.ContentScaling()})`,
-                width: this.props.PanelWidth() ? `${pwidth}px` : `${100 / this.props.ContentScaling()}%`,
-                height: this.props.PanelWidth() ? `${pwidth / aspect}px` : `${100 / this.props.ContentScaling()}%`,
+                width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
+                height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
                 pointerEvents: this.layoutDoc.isBackground ? "none" : undefined,
                 borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
             }} >
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index beb7a448d..1a991f1eb 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -7,15 +7,14 @@ import { observer } from "mobx-react";
 import * as rp from 'request-promise';
 import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Cast, NumCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
 import { emptyFunction, returnFalse, returnOne, Utils, returnZero } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { DocComponent } from "../DocComponent";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ScreenshotBox.scss";
@@ -27,7 +26,7 @@ const ScreenshotDocument = makeInterface(documentSchema, positionSchema);
 library.add(faVideo);
 
 @observer
-export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
+export class ScreenshotBox extends DocComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
     private _reactionDisposer?: IReactionDisposer;
     private _videoRef: HTMLVideoElement | null = null;
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
@@ -38,21 +37,21 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
 
     videoLoad = () => {
         const aspect = this.player!.videoWidth / this.player!.videoHeight;
-        const nativeWidth = (this.Document._nativeWidth || 0);
-        const nativeHeight = (this.Document._nativeHeight || 0);
+        const nativeWidth = (this.layoutDoc._nativeWidth || 0);
+        const nativeHeight = (this.layoutDoc._nativeHeight || 0);
         if (!nativeWidth || !nativeHeight) {
-            if (!this.Document._nativeWidth) this.Document._nativeWidth = 400;
-            this.Document._nativeHeight = (this.Document._nativeWidth || 0) / aspect;
-            this.Document._height = (this.Document._width || 0) / aspect;
+            if (!this.layoutDoc._nativeWidth) this.layoutDoc._nativeWidth = 400;
+            this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / aspect;
+            this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
         }
     }
 
     @action public Snapshot() {
-        const width = this.Document._width || 0;
-        const height = this.Document._height || 0;
+        const width = NumCast(this.layoutDoc._width);
+        const height = NumCast(this.layoutDoc._height);
         const canvas = document.createElement('canvas');
         canvas.width = 640;
-        canvas.height = 640 * (this.Document._nativeHeight || 0) / (this.Document._nativeWidth || 1);
+        canvas.height = 640 * NumCast(this.layoutDoc._nativeHeight) / NumCast(this.layoutDoc._nativeWidth, 1);
         const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
         if (ctx) {
             ctx.rect(0, 0, canvas.width, canvas.height);
@@ -70,7 +69,7 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
                 setTimeout(() => {
                     if (returnedFilename) {
                         const imageSummary = Docs.Create.ImageDocument(Utils.prepend(returnedFilename), {
-                            x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
+                            x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y),
                             _width: 150, _height: height / width * 150, title: "--screenshot--"
                         });
                         this.props.addDocument?.(imageSummary);
@@ -171,16 +170,16 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
                     PanelWidth={this.props.PanelWidth}
                     NativeHeight={returnZero}
                     NativeWidth={returnZero}
-                    annotationsKey={this.annotationKey}
+                    annotationsKey={""}
                     focus={this.props.focus}
                     isSelected={this.props.isSelected}
                     isAnnotationOverlay={true}
                     select={emptyFunction}
-                    active={this.annotationsActive}
+                    active={returnFalse}
                     ContentScaling={returnOne}
-                    whenActiveChanged={this.whenActiveChanged}
-                    removeDocument={this.removeDocument}
-                    moveDocument={this.moveDocument}
+                    whenActiveChanged={emptyFunction}
+                    removeDocument={returnFalse}
+                    moveDocument={returnFalse}
                     addDocument={returnFalse}
                     CollectionView={undefined}
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform}
@@ -189,7 +188,7 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
                     {this.contentFunc}
                 </CollectionFreeFormView>
             </div>
-            {this.active() ? this.uIButtons : (null)}
+            {this.props.isSelected() ? this.uIButtons : (null)}
         </div >);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index f591fd571..2c67883e9 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -9,7 +9,7 @@ import { Doc } from "../../../new_fields/Doc";
 import { InkTool } from "../../../new_fields/InkField";
 import { createSchema, makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
 import { VideoField } from "../../../new_fields/URLField";
 import { Utils, emptyFunction, returnOne, returnZero } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
@@ -125,9 +125,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
                 if (returnedFilename) {
                     const url = this.choosePath(Utils.prepend(returnedFilename));
                     const imageSummary = Docs.Create.ImageDocument(url, {
-                        x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), displayTimecode: this.layoutDoc.currentTimecode || 0,
+                        _nativeWidth: this.layoutDoc._nativeWidth, _nativeHeight: this.layoutDoc._nativeHeight,
+                        x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
                         _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc.currentTimecode || 0) + " image-"
                     });
+                    Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
+                    Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
                     imageSummary.isLinkButton = true;
                     this.props.addDocument && this.props.addDocument(imageSummary);
                     DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot");
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 8a39a7c06..bed181ab9 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -205,7 +205,7 @@ export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresD
                     <strong className="presElementBox-name">
                         {`${this.indexInPres + 1}. ${this.targetDoc?.title}`}
                     </strong>
-                    <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => this.props.removeDocument && this.props.removeDocument(this.presElementDoc)}>X</button>
+                    <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => this.props.removeDocument?.(this.presElementDoc)}>X</button>
                     <br />
                 </>}
                 <button title="Zoom" className={pbi + (this.presElementDoc.zoomButton ? "-selected" : "")} onPointerDown={e => e.stopPropagation()} onClick={this.onZoomDocumentClick}><FontAwesomeIcon icon={"search"} /></button>
-- 
cgit v1.2.3-70-g09d2


From 7b1a4b93be9d01ab5613de09b8f308291f709b01 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 9 Apr 2020 23:27:41 -0400
Subject: fix presbox to work with linked documents. more cleanup to use
 dataDoc/layoutDoc/rootDoc.   changed ## to >> for inline comment to open up
 #### heading markdown

---
 src/client/util/DocumentManager.ts                 |   2 +-
 src/client/util/RichTextRules.ts                   |  25 ++---
 src/client/util/RichTextSchema.tsx                 |  10 +-
 src/client/views/InkingStroke.tsx                  |  17 ++--
 .../views/collections/ParentDocumentSelector.tsx   |   8 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  41 --------
 src/client/views/nodes/AudioBox.tsx                |   2 +-
 src/client/views/nodes/ColorBox.tsx                |   2 +-
 src/client/views/nodes/DocumentView.tsx            |  11 ++-
 src/client/views/nodes/FontIconBox.tsx             |   2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  10 +-
 src/client/views/nodes/FormattedTextBoxComment.tsx |   9 +-
 src/client/views/nodes/ImageBox.tsx                |   7 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |  36 +++----
 src/client/views/nodes/PresBox.tsx                 | 110 +++++++++++----------
 15 files changed, 138 insertions(+), 154 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index fc2f251a5..2d6078cf3 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -148,7 +148,7 @@ export class DocumentManager {
         const highlight = () => {
             const finalDocView = getFirstDocView(targetDoc);
             if (finalDocView) {
-                finalDocView.Document.scrollToLinkID = linkId;
+                finalDocView.layoutDoc.scrollToLinkID = linkId;
                 Doc.linkFollowHighlight(finalDocView.props.Document);
             }
         };
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index b0a124cb8..8f2df054b 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -62,6 +62,17 @@ export class RichTextRules {
             // ``` code block
             textblockTypeInputRule(/^```$/, schema.nodes.code_block),
 
+            // create an inline view of a tag stored under the '#' field
+            new InputRule(
+                new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/),
+                (state, match, start, end) => {
+                    const tag = match[1];
+                    if (!tag) return state.tr;
+                    this.Document[DataSym]["#"] = tag;
+                    const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" });
+                    return state.tr.deleteRange(start, end).insert(start, fieldView);
+                }),
+
             // # heading
             textblockTypeInputRule(
                 new RegExp(/^(#{1,6})\s$/),
@@ -81,7 +92,7 @@ export class RichTextRules {
 
             // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document [[ <fieldKey> : <Doc>]]   // [[:Doc]] => hyperlink   [[fieldKey]] => show field   [[fieldKey:Doc]] => show field of doc
             new InputRule(
-                new RegExp(/\[\[([a-zA-Z_#@\? \-0-9]*)(=[a-zA-Z_#@\? \-0-9]*)?(:[a-zA-Z_#@\? \-0-9]+)?\]\]$/),
+                new RegExp(/\[\[([a-zA-Z_@\? \-0-9]*)(=[a-zA-Z_@\? \-0-9]*)?(:[a-zA-Z_@\? \-0-9]+)?\]\]$/),
                 (state, match, start, end) => {
                     const fieldKey = match[1];
                     const docid = match[3]?.substring(1);
@@ -104,16 +115,6 @@ export class RichTextRules {
                     const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid });
                     return state.tr.deleteRange(start, end).insert(start, fieldView);
                 }),
-            // create an inline view of a tag stored under the '#' field
-            new InputRule(
-                new RegExp(/#([a-zA-Z_\-0-9]+)\s$/),
-                (state, match, start, end) => {
-                    const tag = match[1];
-                    if (!tag) return state.tr;
-                    this.Document[DataSym]["#"] = tag;
-                    const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" });
-                    return state.tr.deleteRange(start, end).insert(start, fieldView);
-                }),
             // create an inline view of a document {{ <layoutKey> : <Doc> }}  // {{:Doc}} => show default view of document   {{<layout>}} => show layout for this doc   {{<layout> : Doc}} => show layout for another doc
             new InputRule(
                 new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._\-]*\))?(:[a-zA-Z_ \-0-9]+)?\}\}$/),
@@ -134,7 +135,7 @@ export class RichTextRules {
                     return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
                 }),
             new InputRule(
-                new RegExp(/##$/),
+                new RegExp(/>>$/),
                 (state, match, start, end) => {
                     const textDoc = this.Document[DataSym];
                     const numInlines = NumCast(textDoc.inlineTextCount);
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3e6cbce56..0599b3ebe 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -27,8 +27,12 @@ import ParagraphNodeSpec from "./ParagraphNodeSpec";
 import { Transform } from "./Transform";
 import React = require("react");
 
-const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
-    preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
+const 
+    blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], 
+    hrDOM: DOMOutputSpecArray = ["hr"],
+    preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], 
+    brDOM: DOMOutputSpecArray = ["br"], 
+    ulDOM: DOMOutputSpecArray = ["ul", 0];
 
 // :: Object
 // [Specs](#model.NodeSpec) for the nodes defined in this schema.
@@ -738,7 +742,7 @@ export class DashDocView {
         this._outer = document.createElement("span");
         this._outer.style.position = "relative";
         this._outer.style.textIndent = "0";
-        this._outer.style.border = "1px solid " + StrCast(tbox.Document.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
         this._outer.style.width = node.attrs.width;
         this._outer.style.height = node.attrs.height;
         this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index db5e2912d..ff63a7c33 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import { documentSchema } from "../../new_fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../new_fields/InkField";
 import { makeInterface } from "../../new_fields/Schema";
-import { Cast, StrCast } from "../../new_fields/Types";
+import { Cast, StrCast, NumCast } from "../../new_fields/Types";
 import { DocExtendableComponent } from "./DocComponent";
 import { InkingControl } from "./InkingControl";
 import "./InkingStroke.scss";
@@ -25,28 +25,27 @@ const InkDocument = makeInterface(documentSchema);
 export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocument>(InkDocument) {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
 
-    @computed get PanelWidth() { return this.props.PanelWidth(); }
-    @computed get PanelHeight() { return this.props.PanelHeight(); }
-
     private analyzeStrokes = () => {
-        const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField) ?.inkData ?? [];
+        const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
         CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]);
     }
 
     render() {
         TraceMobx();
-        const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField) ?.inkData ?? [];
+        const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
         const xs = data.map(p => p.X);
         const ys = data.map(p => p.Y);
         const left = Math.min(...xs);
         const top = Math.min(...ys);
         const right = Math.max(...xs);
         const bottom = Math.max(...ys);
-        const points = InteractionUtils.CreatePolyline(data, left, top, StrCast(this.layoutDoc.color, InkingControl.Instance.selectedColor), this.Document.strokeWidth ?? parseInt(InkingControl.Instance.selectedWidth));
+        const points = InteractionUtils.CreatePolyline(data, left, top,
+            StrCast(this.layoutDoc.color, InkingControl.Instance.selectedColor),
+            NumCast(this.layoutDoc.strokeWidth, parseInt(InkingControl.Instance.selectedWidth)));
         const width = right - left;
         const height = bottom - top;
-        const scaleX = this.PanelWidth / width;
-        const scaleY = this.PanelHeight / height;
+        const scaleX = this.props.PanelWidth() / width;
+        const scaleY = this.props.PanelHeight() / height;
         return (
             <svg
                 width={width}
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index afc1c6754..5c9d2b0dd 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -114,7 +114,13 @@ export class DockingViewButtonSelector extends React.Component<{ views: Document
     }
 
     render() {
-        return <span title="Tap for menu, drag tab as document" onPointerDown={e => { if (getComputedStyle(this._ref.current!).width !== "100%") {e.stopPropagation();e.preventDefault();} this.props.views[0].select(false); }} className="buttonSelector">
+        return <span title="Tap for menu, drag tab as document"
+            onPointerDown={e => {
+                if (getComputedStyle(this._ref.current!).width !== "100%") {
+                    e.stopPropagation(); e.preventDefault();
+                }
+                this.props.views[0]?.select(false);
+            }} className="buttonSelector">
             <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
                 <FontAwesomeIcon icon={"cog"} size={"sm"} />
             </Flyout>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 146ec9f7d..b5bcc0cc2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1017,47 +1017,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
 
     private thumbIdentifier?: number;
 
-    // @action
-    // handleHandDown = (e: React.TouchEvent) => {
-    //     const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true);
-    //     const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
-    //     this.thumbIdentifier = thumb?.identifier;
-    //     const others = fingers.filter(f => f !== thumb);
-    //     const minX = Math.min(...others.map(f => f.clientX));
-    //     const minY = Math.min(...others.map(f => f.clientY));
-    //     const t = this.getTransform().transformPoint(minX, minY);
-    //     const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY);
-
-    //     const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc));
-    //     if (thumbDoc) {
-    //         this._palette = <Palette x={t[0]} y={t[1]} thumb={th} thumbDoc={thumbDoc} />;
-    //     }
-
-    //     document.removeEventListener("touchmove", this.onTouch);
-    //     document.removeEventListener("touchmove", this.handleHandMove);
-    //     document.addEventListener("touchmove", this.handleHandMove);
-    //     document.removeEventListener("touchend", this.handleHandUp);
-    //     document.addEventListener("touchend", this.handleHandUp);
-    // }
-
-    // @action
-    // handleHandMove = (e: TouchEvent) => {
-    //     for (let i = 0; i < e.changedTouches.length; i++) {
-    //         const pt = e.changedTouches.item(i);
-    //         if (pt?.identifier === this.thumbIdentifier) {
-    //         }
-    //     }
-    // }
-
-    // @action
-    // handleHandUp = (e: TouchEvent) => {
-    //     this.onTouchEnd(e);
-    //     if (this.prevPoints.size < 3) {
-    //         this._palette = undefined;
-    //         document.removeEventListener("touchend", this.handleHandUp);
-    //     }
-    // }
-
     onContextMenu = (e: React.MouseEvent) => {
         if (this.props.children && this.props.annotationsKey) return;
         const layoutItems: ContextMenuProps[] = [];
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 9ac41a528..c08a2b808 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -235,7 +235,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
                 <div className="audiobox-controls">
                     <div className="audiobox-player" onClick={this.onPlay}>
                         <div className="audiobox-playhead"> <FontAwesomeIcon style={{ width: "100%" }} icon={this.audioState === "paused" ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
-                        <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%", background: this.Document.playOnSelect ? "yellow" : "dimGray" }} icon="hand-point-left" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
+                        <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%", background: this.layoutDoc.playOnSelect ? "yellow" : "dimGray" }} icon="hand-point-left" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
                         <div className="audiobox-timeline" onClick={e => e.stopPropagation()}
                             onPointerDown={e => {
                                 if (e.button === 0 && !e.ctrlKey) {
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index d34d63d01..b6d687f27 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -19,7 +19,7 @@ export class ColorBox extends DocExtendableComponent<FieldViewProps, ColorDocume
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
 
     render() {
-        const selDoc = SelectionManager.SelectedDocuments()?.[0]?.Document;
+        const selDoc = SelectionManager.SelectedDocuments()?.[0]?.rootDoc;
         return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`}
             onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
             style={{ transformOrigin: "top left", transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 75d635a21..1bccce054 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1024,25 +1024,28 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
 
     @observable _link: Opt<Doc>;  // see DocumentButtonBar for explanation of how this works
-    makeLink = () => { return this._link; } // pass the link placeholde to child views so they can react to make a specialized anchor.  This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined.
+    makeLink = () => this._link; // pass the link placeholde to child views so they can react to make a specialized anchor.  This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined.
 
     @undoBatch
-    hideLinkAnchor = (doc: Doc) => doc.hidden = true;
+    hideLinkAnchor = (doc: Doc) => doc.hidden = true
     anchorPanelWidth = () => this.props.PanelWidth() || 1;
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
     @computed get anchors() {
         TraceMobx();
-        return DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
+        return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
             <div className="documentView-linkAnchorBoxWrapper" key={d[Id]}>
                 <DocumentView {...this.props}
                     Document={d}
+                    ContainingCollectionView={this.props.ContainingCollectionView}
                     ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection  Need a better prop for passing the containing document to the LinkAnchorBox
                     PanelWidth={this.anchorPanelWidth}
                     PanelHeight={this.anchorPanelHeight}
                     layoutKey={this.linkEndpoint(d)}
                     ContentScaling={returnOne}
                     backgroundColor={returnTransparent}
-                    removeDocument={this.hideLinkAnchor} />
+                    removeDocument={this.hideLinkAnchor}
+                    LayoutDoc={undefined}
+                />
             </div>);
     }
     @computed get innards() {
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index d4da21239..bd7ea4c92 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -56,7 +56,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="sm" />
+            <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon} 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 4cccfc966..fb919a716 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -215,7 +215,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     updateTitle = () => {
         if ((this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
-            StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
+            StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.rootDoc.customTitle) {
             const str = this._editorView.state.doc.textContent;
             const titlestr = str.substr(0, Math.min(40, str.length));
             this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
@@ -723,7 +723,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 }
             }, 0);
             dataDoc.title = exportState.title;
-            this.Document.customTitle = true;
+            this.rootDoc.customTitle = true;
             dataDoc.unchanged = true;
         } else {
             delete dataDoc[GoogleRef];
@@ -1155,7 +1155,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 this.layoutDoc.limitHeight = undefined;
                 this.layoutDoc._autoHeight = false;
             }
-            const nh = this.Document.isTemplateForField ? 0 : NumCast(this.dataDoc._nativeHeight, 0);
+            const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.dataDoc._nativeHeight, 0);
             const dh = NumCast(this.layoutDoc._height, 0);
             const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
             if (Math.abs(newHeight - dh) > 1) { // bcz: Argh!  without this, we get into a React crash if the same document is opened in a freeform view and in the treeview.  no idea why, but after dragging the freeform document, selecting it, and selecting text, it will compute to 1 pixel higher than the treeview which causes a cycle
@@ -1205,8 +1205,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }} onScroll={this.onscrolled} ref={this._scrollRef}>
                     <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget}
                         style={{
-                            padding: `${NumCast(this.Document._xMargin, 0)}px  ${NumCast(this.Document._yMargin, 0)}px`,
-                            pointerEvents: ((this.Document.isLinkButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined
+                            padding: `${NumCast(this.layoutDoc._xMargin, 0)}px  ${NumCast(this.layoutDoc._yMargin, 0)}px`,
+                            pointerEvents: ((this.layoutDoc.isLinkButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined
                         }} />
                 </div>
                 {!this.props.Document._showSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 1e48c76e7..35304033f 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -15,6 +15,7 @@ import './FormattedTextBoxComment.scss';
 import React = require("react");
 import { Docs } from "../../documents/Documents";
 import wiki from "wikijs";
+import { DocumentType } from "../../documents/DocumentTypes";
 
 export let formattedTextBoxCommentPlugin = new Plugin({
     view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -83,8 +84,12 @@ export class FormattedTextBoxComment {
                 const keep = e.target && (e.target as any).type === "checkbox" ? true : false;
                 const textBox = FormattedTextBoxComment.textBox;
                 if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
-                    DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
-                        (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+                    if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
+                        textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab":"onRight");
+                    } else {
+                        DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
+                            (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+                    }
                 } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
                     textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), "onRight");
                 }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 55983ab77..bb7d78ace 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -276,8 +276,8 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
                 this.layoutDoc._height = this.layoutDoc[WidthSym]() * cachedAspect;
             }
         } else if (this.layoutDoc._nativeWidth !== cachedNativeSize.width || this.layoutDoc._nativeHeight !== cachedNativeSize.height) {
-            !(this.Document[StrCast(this.props.Document.layoutKey)] instanceof Doc) && setTimeout(() => {
-                if (!(this.Document[StrCast(this.props.Document.layoutKey)] instanceof Doc)) {
+            !(this.layoutDoc[StrCast(this.layoutDoc.layoutKey)] instanceof Doc) && setTimeout(() => {
+                if (!(this.layoutDoc[StrCast(this.layoutDoc.layoutKey)] instanceof Doc)) {
                     this.layoutDoc._nativeWidth = cachedNativeSize.width;
                     this.layoutDoc._nativeHeight = cachedNativeSize.height;
                 }
@@ -432,9 +432,6 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
     contentFunc = () => [this.content];
     render() {
         TraceMobx();
-        const { nativeWidth, nativeHeight } = this.nativeSize;
-        const aspect = nativeWidth / nativeHeight;
-        const pwidth = this.props.PanelWidth() > this.props.PanelHeight() / aspect ? this.props.PanelHeight() / aspect : this.props.PanelWidth();
         const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
         return (<div className={`imageBox${dragging}`} onContextMenu={this.specificContextMenu}
             style={{
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 3e7cf6808..f3e099dd6 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -7,7 +7,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { Utils, setupMoveUpEvents } from '../../../Utils';
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
-import { DocComponent } from "../DocComponent";
+import { DocExtendableComponent } from "../DocComponent";
 import "./LinkAnchorBox.scss";
 import { FieldView, FieldViewProps } from "./FieldView";
 import React = require("react");
@@ -25,7 +25,7 @@ type LinkAnchorSchema = makeInterface<[typeof documentSchema]>;
 const LinkAnchorDocument = makeInterface(documentSchema);
 
 @observer
-export class LinkAnchorBox extends DocComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
+export class LinkAnchorBox extends DocExtendableComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); }
     _doubleTap = false;
     _lastTap: number = 0;
@@ -49,14 +49,14 @@ export class LinkAnchorBox extends DocComponent<FieldViewProps, LinkAnchorSchema
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             const dragdist = Math.sqrt((pt[0] - down[0]) * (pt[0] - down[0]) + (pt[1] - down[1]) * (pt[1] - down[1]));
             if (separation > 100) {
-                const dragData = new DragManager.DocumentDragData([this.props.Document]);
+                const dragData = new DragManager.DocumentDragData([this.rootDoc]);
                 dragData.dropAction = "alias";
                 dragData.removeDropProperties = ["anchor1_x", "anchor1_y", "anchor2_x", "anchor2_y", "isLinkButton"];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, down[0], down[1]);
                 return true;
             } else if (dragdist > separation) {
-                this.props.Document[this.props.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
-                this.props.Document[this.props.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
+                this.layoutDoc[this.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
+                this.layoutDoc[this.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
             }
         }
         return false;
@@ -65,16 +65,16 @@ export class LinkAnchorBox extends DocComponent<FieldViewProps, LinkAnchorSchema
     onClick = (e: PointerEvent) => {
         this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0);
         this._lastTap = Date.now();
-        if ((e.button === 2 || e.ctrlKey || !this.props.Document.isLinkButton)) {
+        if ((e.button === 2 || e.ctrlKey || !this.layoutDoc.isLinkButton)) {
             this.props.select(false);
         }
         if (!this._doubleTap && !e.ctrlKey && e.button < 2) {
             const anchorContainerDoc = this.props.ContainingCollectionDoc; // bcz: hack!  need a better prop for passing the anchor's container 
             this._editing = true;
             anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
-            if (anchorContainerDoc && !this.props.Document.onClick && !this._isOpen) {
+            if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
                 this._timeout = setTimeout(action(() => {
-                    DocumentManager.Instance.FollowLink(this.props.Document, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false);
+                    DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "inTab")), false);
                     this._editing = false;
                 }), 300 - (Date.now() - this._lastTap));
             }
@@ -85,10 +85,10 @@ export class LinkAnchorBox extends DocComponent<FieldViewProps, LinkAnchorSchema
     }
 
     openLinkDocOnRight = (e: React.MouseEvent) => {
-        this.props.addDocTab(this.props.Document, "onRight");
+        this.props.addDocTab(this.rootDoc, "onRight");
     }
     openLinkTargetOnRight = (e: React.MouseEvent) => {
-        const alias = Doc.MakeAlias(Cast(this.props.Document[this.props.fieldKey], Doc, null));
+        const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias.isLinkButton = undefined;
         alias.isBackground = undefined;
         alias.layoutKey = "layout";
@@ -111,17 +111,17 @@ export class LinkAnchorBox extends DocComponent<FieldViewProps, LinkAnchorSchema
 
     render() {
         TraceMobx();
-        const x = this.props.PanelWidth() > 1 ? NumCast(this.props.Document[this.props.fieldKey + "_x"], 100) : 0;
-        const y = this.props.PanelWidth() > 1 ? NumCast(this.props.Document[this.props.fieldKey + "_y"], 100) : 0;
-        const c = StrCast(this.props.Document.backgroundColor, "lightblue");
-        const anchor = this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1";
+        const x = this.props.PanelWidth() > 1 ? NumCast(this.layoutDoc[this.fieldKey + "_x"], 100) : 0;
+        const y = this.props.PanelWidth() > 1 ? NumCast(this.layoutDoc[this.fieldKey + "_y"], 100) : 0;
+        const c = StrCast(this.layoutDoc.backgroundColor, "lightblue");
+        const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
         const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .15;
 
-        const timecode = this.props.Document[anchor + "Timecode"];
-        const targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : "");
+        const timecode = this.dataDoc[anchor + "_timecode"];
+        const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title) + (timecode !== undefined ? ":" + timecode : "");
         const flyout = (
-            <div className="linkAnchorBoxBox-flyout" title=" " onPointerOver={() => Doc.UnBrushDoc(this.props.Document)}>
-                <LinkEditor sourceDoc={Cast(this.props.Document[this.props.fieldKey], Doc, null)} hideback={true} linkDoc={this.props.Document} showLinks={action(() => { })} />
+            <div className="linkAnchorBoxBox-flyout" title=" " onPointerOver={() => Doc.UnBrushDoc(this.rootDoc)}>
+                <LinkEditor sourceDoc={Cast(this.dataDoc[this.fieldKey], Doc, null)} hideback={true} linkDoc={this.rootDoc} showLinks={action(() => { })} />
                 {!this._forceOpen ? (null) : <div className="linkAnchorBox-linkCloser" onPointerDown={action(() => this._isOpen = this._editing = this._forceOpen = false)}>
                     <FontAwesomeIcon color="dimGray" icon={"times"} size={"sm"} />
                 </div>}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index e7434feaa..bea3170ac 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -4,10 +4,11 @@ import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faH
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc, DocListCast, DocCastAsync } from "../../../new_fields/Doc";
 import { InkTool } from "../../../new_fields/InkField";
 import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
 import { returnFalse } from "../../../Utils";
+import { documentSchema } from "../../../new_fields/documentSchemas";
 import { DocumentManager } from "../../util/DocumentManager";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -15,6 +16,8 @@ import { CollectionView, CollectionViewType } from "../collections/CollectionVie
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./PresBox.scss";
+import { DocExtendableComponent } from "../DocComponent";
+import { makeInterface } from "../../../new_fields/Schema";
 
 library.add(faArrowLeft);
 library.add(faArrowRight);
@@ -26,24 +29,27 @@ library.add(faTimes);
 library.add(faMinus);
 library.add(faEdit);
 
+type PresBoxSchema = makeInterface<[typeof documentSchema]>;
+const PresBoxDocument = makeInterface(documentSchema);
+
 @observer
-export class PresBox extends React.Component<FieldViewProps> {
+export class PresBox extends DocExtendableComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
     _childReaction: IReactionDisposer | undefined;
     @observable _isChildActive = false;
     componentDidMount() {
-        this.props.Document._forceRenderEngine = "timeline";
-        this.props.Document._replacedChrome = "replaced";
+        this.layoutDoc._forceRenderEngine = "timeline";
+        this.layoutDoc._replacedChrome = "replaced";
         this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true });
     }
     componentWillUnmount() {
         this._childReaction?.();
     }
 
-    @computed get childDocs() { return DocListCast(this.props.Document[this.props.fieldKey]); }
-    @computed get currentIndex() { return NumCast(this.props.Document._itemIndex); }
+    @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
+    @computed get currentIndex() { return NumCast(this.layoutDoc._itemIndex); }
 
-    updateCurrentPresentation = action(() => Doc.UserDoc().curPresentation = this.props.Document);
+    updateCurrentPresentation = action(() => Doc.UserDoc().curPresentation = this.rootDoc);
 
     next = () => {
         this.updateCurrentPresentation();
@@ -78,8 +84,8 @@ export class PresBox extends React.Component<FieldViewProps> {
     }
 
     whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
-    active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) &&
-        (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
+    active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.layoutDoc.isBackground) &&
+        (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
      * This is the method that checks for the actions that need to be performed
@@ -131,11 +137,10 @@ export class PresBox extends React.Component<FieldViewProps> {
      */
     navigateToElement = async (curDoc: Doc, fromDocIndex: number) => {
         this.updateCurrentPresentation();
-        const fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc;
         let docToJump = curDoc;
         let willZoom = false;
 
-        const presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
+        const presDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
         let nextSelected = presDocs.indexOf(curDoc);
         const currentDocGroups: Doc[] = [];
         for (; nextSelected < presDocs.length - 1; nextSelected++) {
@@ -157,29 +162,28 @@ export class PresBox extends React.Component<FieldViewProps> {
         });
 
         //docToJump stayed same meaning, it was not in the group or was the last element in the group
-        const aliasOf = await Cast(docToJump.aliasOf, Doc);
-        const srcContext = aliasOf && await Cast(aliasOf.context, Doc);
+        const aliasOf = await DocCastAsync(docToJump.aliasOf);
+        const srcContext = aliasOf && await DocCastAsync(aliasOf.context);
         if (docToJump === curDoc) {
             //checking if curDoc has navigation open
-            const target = await Cast(curDoc.presentationTargetDoc, Doc);
+            const target = await DocCastAsync(curDoc.presentationTargetDoc);
             if (curDoc.navButton && target) {
                 DocumentManager.Instance.jumpToDocument(target, false, undefined, srcContext);
             } else if (curDoc.zoomButton && target) {
                 //awaiting jump so that new scale can be found, since jumping is async
                 await DocumentManager.Instance.jumpToDocument(target, true, undefined, srcContext);
             }
-            return;
+        } else {
+            //awaiting jump so that new scale can be found, since jumping is async
+            const presTargetDoc = await DocCastAsync(docToJump.presentationTargetDoc);
+            presTargetDoc && await DocumentManager.Instance.jumpToDocument(presTargetDoc, willZoom, undefined, srcContext);
         }
-
-        //awaiting jump so that new scale can be found, since jumping is async
-        const presTargetDoc = await docToJump.presentationTargetDoc as Doc;
-        await DocumentManager.Instance.jumpToDocument(presTargetDoc, willZoom, undefined, srcContext);
     }
 
 
     @undoBatch
     public removeDocument = (doc: Doc) => {
-        return Doc.RemoveDocFromList(this.props.Document, this.props.fieldKey, doc);
+        return Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
     }
 
     //The function that is called when a document is clicked or reached through next or back.
@@ -188,10 +192,10 @@ export class PresBox extends React.Component<FieldViewProps> {
         this.updateCurrentPresentation();
         Doc.UnBrushAllDocs();
         if (index >= 0 && index < this.childDocs.length) {
-            this.props.Document._itemIndex = index;
+            this.layoutDoc._itemIndex = index;
 
-            if (!this.props.Document.presStatus) {
-                this.props.Document.presStatus = true;
+            if (!this.layoutDoc.presStatus) {
+                this.layoutDoc.presStatus = true;
                 this.startPresentation(index);
             }
 
@@ -204,10 +208,10 @@ export class PresBox extends React.Component<FieldViewProps> {
     //The function that starts or resets presentaton functionally, depending on status flag.
     startOrResetPres = () => {
         this.updateCurrentPresentation();
-        if (this.props.Document.presStatus) {
+        if (this.layoutDoc.presStatus) {
             this.resetPresentation();
         } else {
-            this.props.Document.presStatus = true;
+            this.layoutDoc.presStatus = true;
             this.startPresentation(0);
             this.gotoDocument(0, this.currentIndex);
         }
@@ -216,7 +220,7 @@ export class PresBox extends React.Component<FieldViewProps> {
     addDocument = (doc: Doc) => {
         const newPinDoc = Doc.MakeAlias(doc);
         newPinDoc.presentationTargetDoc = doc;
-        return Doc.AddDocToList(this.props.Document, this.props.fieldKey, newPinDoc);
+        return Doc.AddDocToList(this.dataDoc, this.fieldKey, newPinDoc);
     }
 
 
@@ -225,8 +229,8 @@ export class PresBox extends React.Component<FieldViewProps> {
     resetPresentation = () => {
         this.updateCurrentPresentation();
         this.childDocs.forEach(doc => (doc.presentationTargetDoc as Doc).opacity = 1);
-        this.props.Document._itemIndex = 0;
-        this.props.Document.presStatus = false;
+        this.layoutDoc._itemIndex = 0;
+        this.layoutDoc.presStatus = false;
     }
 
     //The function that starts the presentation, also checking if actions should be applied
@@ -247,16 +251,16 @@ export class PresBox extends React.Component<FieldViewProps> {
     }
 
     updateMinimize = undoBatch(action((e: React.ChangeEvent, mode: CollectionViewType) => {
-        if (BoolCast(this.props.Document.inOverlay) !== (mode === CollectionViewType.Invalid)) {
-            if (this.props.Document.inOverlay) {
-                Doc.RemoveDocFromList((Doc.UserDoc().overlays as Doc), undefined, this.props.Document);
-                CollectionDockingView.AddRightSplit(this.props.Document);
-                this.props.Document.inOverlay = false;
+        if (BoolCast(this.layoutDoc.inOverlay) !== (mode === CollectionViewType.Invalid)) {
+            if (this.layoutDoc.inOverlay) {
+                Doc.RemoveDocFromList((Doc.UserDoc().overlays as Doc), undefined, this.rootDoc);
+                CollectionDockingView.AddRightSplit(this.rootDoc);
+                this.layoutDoc.inOverlay = false;
             } else {
-                this.props.Document.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25;
-                this.props.Document.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25;
-                this.props.addDocTab?.(this.props.Document, "close");
-                Doc.AddDocToList((Doc.UserDoc().overlays as Doc), undefined, this.props.Document);
+                this.layoutDoc.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25;
+                this.layoutDoc.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25;
+                this.props.addDocTab?.(this.rootDoc, "close");
+                Doc.AddDocToList((Doc.UserDoc().overlays as Doc), undefined, this.rootDoc);
             }
         }
     }));
@@ -264,13 +268,13 @@ export class PresBox extends React.Component<FieldViewProps> {
     initializeViewAliases = (docList: Doc[], viewtype: CollectionViewType) => {
         const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46;
         docList.forEach(doc => {
-            doc.presBox = this.props.Document; // give contained documents a reference to the presentation
+            doc.presBox = this.rootDoc; // give contained documents a reference to the presentation
             doc.collapsedHeight = hgt;  //  set the collpased height for documents based on the type of view (Tree or Stack) they will be displaye din
         });
     }
 
     selectElement = (doc: Doc) => {
-        this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.props.Document._itemIndex));
+        this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.layoutDoc._itemIndex));
     }
 
     getTransform = () => {
@@ -283,17 +287,17 @@ export class PresBox extends React.Component<FieldViewProps> {
     @undoBatch
     viewChanged = action((e: React.ChangeEvent) => {
         //@ts-ignore
-        this.props.Document._viewType = e.target.selectedOptions[0].value;
-        this.props.Document._viewType === CollectionViewType.Stacking && (this.props.Document._pivotField = undefined); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
-        this.updateMinimize(e, Number(this.props.Document._viewType));
+        this.layoutDoc._viewType = e.target.selectedOptions[0].value;
+        this.layoutDoc._viewType === CollectionViewType.Stacking && (this.layoutDoc._pivotField = undefined); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
+        this.updateMinimize(e, StrCast(this.layoutDoc._viewType));
     });
 
-    childLayoutTemplate = () => this.props.Document._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined;
+    childLayoutTemplate = () => this.layoutDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined;
     render() {
-        const mode = StrCast(this.props.Document._viewType) as CollectionViewType;
+        const mode = StrCast(this.layoutDoc._viewType) as CollectionViewType;
         this.initializeViewAliases(this.childDocs, mode);
-        return <div className="presBox-cont" style={{ minWidth: this.props.Document.inOverlay ? 240 : undefined, pointerEvents: this.active() || this.props.Document.inOverlay ? "all" : "none" }} >
-            <div className="presBox-buttons" style={{ display: this.props.Document._chromeStatus === "disabled" ? "none" : undefined }}>
+        return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined, pointerEvents: this.active() || this.layoutDoc.inOverlay ? "all" : "none" }} >
+            <div className="presBox-buttons" style={{ display: this.layoutDoc._chromeStatus === "disabled" ? "none" : undefined }}>
                 <select className="collectionViewBaseChrome-viewPicker"
                     onPointerDown={e => e.stopPropagation()}
                     onChange={this.viewChanged}
@@ -304,15 +308,21 @@ export class PresBox extends React.Component<FieldViewProps> {
                     <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel}>Slides</option>
                 </select>
                 <button className="presBox-button" title="Back" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
-                <button className="presBox-button" title={"Reset Presentation" + this.props.Document.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}>
-                    <FontAwesomeIcon icon={this.props.Document.presStatus ? "stop" : "play"} />
+                <button className="presBox-button" title={"Reset Presentation" + this.layoutDoc.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}>
+                    <FontAwesomeIcon icon={this.layoutDoc.presStatus ? "stop" : "play"} />
                 </button>
                 <button className="presBox-button" title="Next" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
             </div>
             <div className="presBox-listCont" >
                 {mode !== CollectionViewType.Invalid ?
-                    <CollectionView {...this.props} PanelHeight={this.panelHeight} moveDocument={returnFalse} childLayoutTemplate={this.childLayoutTemplate}
-                        addDocument={this.addDocument} removeDocument={returnFalse} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} />
+                    <CollectionView {...this.props}
+                        PanelHeight={this.panelHeight}
+                        moveDocument={returnFalse}
+                        childLayoutTemplate={this.childLayoutTemplate}
+                        addDocument={this.addDocument}
+                        removeDocument={returnFalse}
+                        focus={this.selectElement}
+                        ScreenToLocalTransform={this.getTransform} />
                     : (null)
                 }
             </div>
-- 
cgit v1.2.3-70-g09d2


From e4b22138e39244728144fbdd4c06e439be1b519a Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 10 Apr 2020 13:24:07 -0400
Subject: more cleanup for layoutDoc/dataDoc/rootDoc updates. fix to doc
 filtering checkboxes getting filtered. fix to template captions.  fixes to
 generalize LabelDoc's buttonParms

---
 src/client/documents/Documents.ts                  | 10 ++---
 src/client/util/DragManager.ts                     |  2 +-
 src/client/util/DropConverter.ts                   |  2 +-
 src/client/util/RichTextRules.ts                   |  2 +-
 src/client/views/DocComponent.tsx                  | 16 +++----
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/InkingStroke.tsx                  |  4 +-
 src/client/views/TemplateMenu.tsx                  |  6 +--
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 .../views/collections/CollectionTreeView.tsx       | 16 ++++---
 src/client/views/collections/CollectionView.tsx    | 17 ++++----
 .../views/collections/CollectionViewChromes.tsx    | 23 ++--------
 src/client/views/nodes/AudioBox.tsx                |  8 ++--
 src/client/views/nodes/ColorBox.tsx                |  4 +-
 src/client/views/nodes/DocumentBox.tsx             |  6 +--
 src/client/views/nodes/DocumentView.tsx            |  6 +--
 src/client/views/nodes/FontIconBox.tsx             |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +-
 src/client/views/nodes/ImageBox.tsx                |  4 +-
 src/client/views/nodes/LabelBox.tsx                | 48 +++++++++-----------
 src/client/views/nodes/LinkAnchorBox.tsx           |  4 +-
 src/client/views/nodes/LinkBox.tsx                 |  4 +-
 src/client/views/nodes/PDFBox.tsx                  |  4 +-
 src/client/views/nodes/PresBox.tsx                 |  4 +-
 src/client/views/nodes/QueryBox.tsx                |  4 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  6 +--
 src/client/views/nodes/ScriptingBox.tsx            |  4 +-
 src/client/views/nodes/SliderBox.tsx               | 51 ++++++++++------------
 src/client/views/nodes/VideoBox.tsx                |  4 +-
 src/client/views/nodes/WebBox.tsx                  |  4 +-
 src/client/views/pdf/PDFViewer.tsx                 |  4 +-
 .../views/presentationview/PresElementBox.tsx      |  4 +-
 src/new_fields/Doc.ts                              |  9 ++--
 src/new_fields/RichTextField.ts                    |  4 ++
 34 files changed, 137 insertions(+), 159 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c823c3233..22f22cbfd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -566,8 +566,8 @@ export namespace Docs {
 
             LinkManager.Instance.addLink(doc);
 
-            Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)");
-            Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)");
+            Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)");
+            Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)");
             return doc;
         }
 
@@ -908,10 +908,10 @@ export namespace DocUtils {
         if (target.doc === CurrentUserUtils.UserDocument) return undefined;
 
         const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship }, id);
-        Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('this.anchor1.title +" (" + (this.linkRelationship||"to") +") "  + this.anchor2.title');
+        Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('self.anchor1.title +" (" + (self.linkRelationship||"to") +") "  + self.anchor2.title');
 
-        Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)");
-        Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)");
+        Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)");
+        Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)");
         return linkDoc;
     }
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c856bbb1e..346eb2fb4 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -222,7 +222,7 @@ export namespace DragManager {
             const bd = Docs.Create.ButtonDocument({ _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
             params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
             initialize?.(bd);
-            bd.buttonParams = new List<string>(params);
+            bd["onclick-params"] = new List<string>(params);
             e.docDragData && (e.docDragData.droppedDocuments = [bd]);
         };
         StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag);
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index eb4d3a9b0..8cea120cd 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -35,7 +35,7 @@ export function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string
             any = makeTemplate(d, false) || any;
         }
     });
-    if (!docs.length && first) {
+    if (first) {
         any = Doc.MakeMetadataFieldTemplate(doc, Doc.GetProto(layoutDoc)) || any;
     }
     if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 8f2df054b..4ccd461be 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -147,7 +147,7 @@ export class RichTextRules {
                     textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
                     textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
                     textDocInline.proto = textDoc;  // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
-                    textDocInline._textContext = ComputedField.MakeFunction(`copyField(this.${inlineFieldKey})`, { this: Doc.name });
+                    textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
                     textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
                     textDoc[inlineFieldKey] = ""; // set a default value for the annotation
                     const node = (state.doc.resolve(start) as any).nodeAfter;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index d08065820..c97e46f91 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -8,7 +8,7 @@ import { InkTool } from '../../new_fields/InkField';
 import { InteractionUtils } from '../util/InteractionUtils';
 
 
-///  DocComponent returns a generic React base class used by views that don't have any data extensions (e.g.,CollectionFreeFormDocumentView, DocumentView, LabelBox)
+///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 interface DocComponentProps {
     Document: Doc;
     LayoutDoc?: () => Opt<Doc>;
@@ -22,15 +22,15 @@ export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: D
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        @computed get dataDoc() { return this.props.Document[DataSym]; }
+        @computed get dataDoc() { return this.props.Document[DataSym] as Doc; }
 
         protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
     return Component;
 }
 
-///  DocStaticProps return a base class for React document views that have data extensions but aren't annotatable (e.g. AudioBox, FormattedTextBox)
-interface DocExtendableProps {
+/// FieldViewBoxProps  -  a generic base class for field views that are not annotatable (e.g. AudioBox, FormattedTextBox)
+interface ViewBoxBaseProps {
     Document: Doc;
     DataDoc?: Doc;
     fieldKey: string;
@@ -38,7 +38,7 @@ interface DocExtendableProps {
     renderDepth: number;
     rootSelected: (outsideReaction?: boolean) => boolean;
 }
-export function DocExtendableComponent<P extends DocExtendableProps, T>(schemaCtor: (doc: Doc) => T) {
+export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         //@computed get Document(): T { return schemaCtor(this.props.Document); }
@@ -60,8 +60,8 @@ export function DocExtendableComponent<P extends DocExtendableProps, T>(schemaCt
 }
 
 
-///  DocAnnotatbleComponent return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image)
-export interface DocAnnotatableProps {
+///  DocAnnotatbleComponent -return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image)
+export interface ViewBoxAnnotatableProps {
     Document: Doc;
     DataDoc?: Doc;
     fieldKey: string;
@@ -71,7 +71,7 @@ export interface DocAnnotatableProps {
     rootSelected: (outsideReaction?: boolean) => boolean;
     renderDepth: number;
 }
-export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
+export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
     class Component extends Touchable<P> {
         @observable _isChildActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index bd72385ef..c49fe157c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -393,7 +393,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (SelectionManager.SelectedDocuments().length === 1) {
             const selected = SelectionManager.SelectedDocuments()[0];
             if (this._titleControlString.startsWith("=")) {
-                return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ this: selected.props.Document }, console.log).result?.toString() || "";
+                return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || "";
             }
             if (this._titleControlString.startsWith("#")) {
                 return selected.props.Document[this._titleControlString.substring(1)]?.toString() || "-unset-";
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index ff63a7c33..f66c04e1f 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -4,7 +4,7 @@ import { documentSchema } from "../../new_fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../new_fields/InkField";
 import { makeInterface } from "../../new_fields/Schema";
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
-import { DocExtendableComponent } from "./DocComponent";
+import { ViewBoxBaseComponent } from "./DocComponent";
 import { InkingControl } from "./InkingControl";
 import "./InkingStroke.scss";
 import { FieldView, FieldViewProps } from "./nodes/FieldView";
@@ -22,7 +22,7 @@ type InkDocument = makeInterface<[typeof documentSchema]>;
 const InkDocument = makeInterface(documentSchema);
 
 @observer
-export class InkingStroke extends DocExtendableComponent<FieldViewProps, InkDocument>(InkDocument) {
+export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
 
     private analyzeStrokes = () => {
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 8fb8e7516..4d7f1e443 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -76,7 +76,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @undoBatch
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        this.props.docViews.forEach(d => Doc.Layout(d.Document)["_show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : "");
+        this.props.docViews.forEach(d => Doc.Layout(d.layoutDoc)["_show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : "");
     }
 
     @action
@@ -87,7 +87,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     @undoBatch
     @action
     toggleChrome = (): void => {
-        this.props.docViews.map(dv => Doc.Layout(dv.Document)).forEach(layout =>
+        this.props.docViews.map(dv => Doc.Layout(dv.layoutDoc)).forEach(layout =>
             layout._chromeStatus = (layout._chromeStatus !== "disabled" ? "disabled" : StrCast(layout._replacedChrome, "enabled")));
     }
 
@@ -124,7 +124,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
         templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
         if (noteTypesDoc) {
-            addedTypes.concat(noteTypes).map(template => template.treeViewChecked = ComputedField.MakeFunction(`templateIsUsed(this)`));
+            addedTypes.concat(noteTypes).map(template => template.treeViewChecked = ComputedField.MakeFunction(`templateIsUsed(self)`));
             this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push(
                 <OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
         }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 41f4fb3f0..0c00a1f22 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -120,8 +120,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             return Cast(this.dataField, listSpec(Doc));
         }
         @computed get childDocs() {
-            const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
-            const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+            const docFilters = this.props.ignoreFields?.includes("_docFilters") ? [] : Cast(this.props.Document._docFilters, listSpec("string"), []);
+            const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
             const filterFacets: { [key: string]: { [value: string]: string } } = {};  // maps each filter key to an object with value=>modifier fields
             for (let i = 0; i < docFilters.length; i += 3) {
                 const [key, value, modifiers] = docFilters.slice(i, i + 3);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 7edda5a4f..fd84c6498 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -735,16 +735,18 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
                 heroView._showTitle = "title";
                 heroView._showTitleHover = "titlehover";
 
-                Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data",
+                Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data",
                     Docs.Create.FontIconDocument({
-                        _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
-                        dragFactory: heroView, removeDropProperties: new List<string>(["dropAction"]), title: "hero view", icon: "portrait"
+                        title: "hero view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
+                        dragFactory: heroView, removeDropProperties: new List<string>(["dropAction"]), icon: "portrait",
+                        onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
                     }));
 
-                Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data",
+                Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data",
                     Docs.Create.FontIconDocument({
-                        _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
-                        dragFactory: detailView, removeDropProperties: new List<string>(["dropAction"]), title: "detail view", icon: "file-alt"
+                        title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
+                        dragFactory: detailView, removeDropProperties: new List<string>(["dropAction"]), icon: "file-alt",
+                        onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
                     }));
 
                 Document.childLayout = heroView;
@@ -837,7 +839,7 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey
         Docs.Create.TextDocument("", {
             title: facetValue.toString(),
             treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",
-                { layoutDoc: Doc.name, facetHeader: "string", facetValue: "string" },
+                {},
                 { layoutDoc, facetHeader, facetValue })
         }));
     return new List<Doc>(facetValueDocSet);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 8192e6751..821840e0b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -354,14 +354,15 @@ export class CollectionView extends Touchable<FieldViewProps> {
             let newFacet: Opt<Doc>;
             if (nonNumbers / allCollectionDocs.length < .1) {
                 newFacet = Docs.Create.SliderDocument({ title: facetHeader });
+                const newFacetField = Doc.LayoutFieldKey(newFacet);
                 const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader);
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
                 newFacet.treeViewExpandedView = "layout";
                 newFacet.treeViewOpen = true;
-                newFacet._sliderMin = ranged === undefined ? minVal : ranged[0];
-                newFacet._sliderMax = ranged === undefined ? maxVal : ranged[1];
-                newFacet._sliderMinThumb = minVal;
-                newFacet._sliderMaxThumb = maxVal;
+                newFacet[newFacetField + "-min"] = ranged === undefined ? minVal : ranged[0];
+                newFacet[newFacetField + "-max"] = ranged === undefined ? maxVal : ranged[1];
+                Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = minVal;
+                Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = maxVal;
                 newFacet.target = this.props.Document;
                 const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`;
                 newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
@@ -370,14 +371,12 @@ export class CollectionView extends Touchable<FieldViewProps> {
             } else {
                 newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true });
                 const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
-                const params = { layoutDoc: Doc.name, dataDoc: Doc.name, };
-                newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables);
+                newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables);
             }
             newFacet && Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
         }
     }
 
-
     onPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
             this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
@@ -385,11 +384,11 @@ export class CollectionView extends Touchable<FieldViewProps> {
         }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0));
     }
     filterBackground = () => "dimGray";
+    get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
     @computed get scriptField() {
         const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
         return ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
     }
-    @computed get treeIgnoreFields() { return ["_facetCollection", "_docFilters"]; }
     @computed get filterView() {
         const facetCollection = this.props.Document;
         const flyout = (
@@ -419,7 +418,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
                         NativeWidth={returnZero}
                         treeViewHideHeaderFields={true}
                         onCheckedClick={this.scriptField!}
-                        ignoreFields={this.treeIgnoreFields}
+                        ignoreFields={this.ignoreFields}
                         annotationsKey={""}
                         dontRegisterView={true}
                         PanelWidth={this.facetWidth}
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index b2ca3c19f..ba95dce00 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -361,28 +361,13 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         }, emptyFunction, emptyFunction);
     }
     dragCommandDown = (e: React.PointerEvent) => {
-        this._startDragPosition = { x: e.clientX, y: e.clientY };
-        document.addEventListener("pointermove", this.dragPointerMove);
-        document.addEventListener("pointerup", this.dragPointerUp);
-        e.stopPropagation();
-        e.preventDefault();
-    }
-
-    dragPointerMove = (e: PointerEvent) => {
-        e.stopPropagation();
-        e.preventDefault();
-        const [dx, dy] = [e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y];
-        if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
+        setupMoveUpEvents(this, e, (e, down, delta) => {
             this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c =>
                 DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
                     { target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY));
-            document.removeEventListener("pointermove", this.dragPointerMove);
-            document.removeEventListener("pointerup", this.dragPointerUp);
-        }
-    }
-    dragPointerUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.dragPointerMove);
-        document.removeEventListener("pointerup", this.dragPointerUp);
+            return true;
+        }, emptyFunction, emptyFunction);
+        this._startDragPosition = { x: e.clientX, y: e.clientY };
     }
 
     render() {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index c08a2b808..7078cc01c 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
 import "./AudioBox.scss";
 import { Cast, DateCast, NumCast } from "../../../new_fields/Types";
 import { AudioField, nullAudio } from "../../../new_fields/URLField";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
 import { makeInterface, createSchema } from "../../../new_fields/Schema";
 import { documentSchema } from "../../../new_fields/documentSchemas";
 import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse, returnZero } from "../../../Utils";
@@ -39,7 +39,7 @@ type AudioDocument = makeInterface<[typeof documentSchema, typeof audioSchema]>;
 const AudioDocument = makeInterface(documentSchema, audioSchema);
 
 @observer
-export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocument>(AudioDocument) {
+export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument>(AudioDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); }
     public static Enabled = false;
 
@@ -194,8 +194,8 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
             _width: NumCast(this.props.Document._width), _height: 3 * NumCast(this.props.Document._height)
         });
         Doc.GetProto(newDoc).recordingSource = this.dataDoc;
-        Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`this.recordingSource["${this.props.fieldKey}-recordingStart"]`);
-        Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("this.recordingSource.audioState");
+        Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
+        Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("self.recordingSource.audioState");
         this.props.addDocument?.(newDoc);
         e.stopPropagation();
     }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index b6d687f27..877dfec2d 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -6,7 +6,7 @@ import { makeInterface } from "../../../new_fields/Schema";
 import { StrCast } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { SelectionManager } from "../../util/SelectionManager";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
 import { InkingControl } from "../InkingControl";
 import "./ColorBox.scss";
 import { FieldView, FieldViewProps } from './FieldView';
@@ -15,7 +15,7 @@ type ColorDocument = makeInterface<[typeof documentSchema]>;
 const ColorDocument = makeInterface(documentSchema);
 
 @observer
-export class ColorBox extends DocExtendableComponent<FieldViewProps, ColorDocument>(ColorDocument) {
+export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument>(ColorDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
 
     render() {
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index 4f2b3b656..ac562f19a 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -9,7 +9,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { emptyPath } from "../../../Utils";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
 import "./DocumentBox.scss";
 import { FieldView, FieldViewProps } from "./FieldView";
@@ -22,7 +22,7 @@ type DocHolderBoxSchema = makeInterface<[typeof documentSchema]>;
 const DocHolderBoxDocument = makeInterface(documentSchema);
 
 @observer
-export class DocHolderBox extends DocAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
+export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); }
     _prevSelectionDisposer: IReactionDisposer | undefined;
     _selections: Doc[] = [];
@@ -54,7 +54,7 @@ export class DocHolderBox extends DocAnnotatableComponent<FieldViewProps, DocHol
         this.contentDoc[this.props.fieldKey] = this.props.Document[this.props.fieldKey];
     }
     showSelection = () => {
-        this.contentDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(this,this.excludeCollections,[_last_])?.[0]`);
+        this.contentDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`);
     }
     isSelectionLocked = () => {
         const kvpstring = Field.toKeyValueString(this.contentDoc, this.props.fieldKey);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1bccce054..1e22dbc0b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -298,7 +298,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             } else if (this.onClickHandler?.script) {
                 SelectionManager.DeselectAll();
                 const func = () => this.onClickHandler.script.run({
-                    this: this.props.Document,
+                    this: this.layoutDoc,
                     self: this.rootDoc,
                     thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
                 }, console.log);
@@ -512,8 +512,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     onPointerUp = (e: PointerEvent): void => {
         this.cleanUpInteractions();
 
-        if (this.onPointerUpHandler && this.onPointerUpHandler.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-            this.onPointerUpHandler.script.run({ this: this.Document.isTemplateForField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log);
+        if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+            this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
             document.removeEventListener("pointerup", this.onPointerUp);
             return;
         }
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index bd7ea4c92..9329cf210 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -56,7 +56,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.dataDoc.icon} color={this._foregroundColor} size="sm" />
+            <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.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 fb919a716..d641dc791 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -38,7 +38,7 @@ import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentButtonBar } from '../DocumentButtonBar';
 import { InkingControl } from "../InkingControl";
 import { AudioBox } from './AudioBox';
@@ -69,7 +69,7 @@ const RichTextDocument = makeInterface(richTextSchema, documentSchema);
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
 @observer
-export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); }
     public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
     public static Instance: FormattedTextBox;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index bb7d78ace..5b0edf34f 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -24,7 +24,7 @@ import { undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from "../../views/ContextMenu";
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocAnnotatableComponent } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import FaceRectangles from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
@@ -65,7 +65,7 @@ const uploadIcons = {
 };
 
 @observer
-export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) {
+export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) {
     protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
     private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 0ec6af93a..b9701f923 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -7,10 +7,10 @@ import { Doc, DocListCast } from '../../../new_fields/Doc';
 import { List } from '../../../new_fields/List';
 import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
 import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, StrCast, Cast, FieldValue } from '../../../new_fields/Types';
+import { BoolCast, StrCast, Cast, FieldValue, NumCast } from '../../../new_fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
-import { DocComponent } from '../DocComponent';
+import { ViewBoxBaseComponent } from '../DocComponent';
 import './LabelBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
 import { ContextMenuProps } from '../ContextMenuItem';
@@ -20,27 +20,16 @@ import { documentSchema } from '../../../new_fields/documentSchemas';
 
 library.add(faEdit as any);
 
-const LabelSchema = createSchema({
-    onClick: ScriptField,
-    buttonParams: listSpec("string"),
-    text: "string"
-});
+const LabelSchema = createSchema({});
 
 type LabelDocument = makeInterface<[typeof LabelSchema, typeof documentSchema]>;
 const LabelDocument = makeInterface(LabelSchema, documentSchema);
 
 @observer
-export class LabelBox extends DocComponent<FieldViewProps, LabelDocument>(LabelDocument) {
+export class LabelBox extends ViewBoxBaseComponent<FieldViewProps, LabelDocument>(LabelDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); }
     private dropDisposer?: DragManager.DragDropDisposer;
 
-    @computed get dataDoc() {
-        return this.props.DataDoc &&
-            (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) ||
-                this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document);
-    }
-
-
     protected createDropTarget = (ele: HTMLDivElement) => {
         this.dropDisposer?.();
         if (ele) {
@@ -52,8 +41,8 @@ export class LabelBox extends DocComponent<FieldViewProps, LabelDocument>(LabelD
         const funcs: ContextMenuProps[] = [];
         funcs.push({
             description: "Clear Script Params", event: () => {
-                const params = FieldValue(this.Document.buttonParams);
-                params?.map(p => this.props.Document[p] = undefined);
+                const params = Cast(this.dataDoc[this.fieldKey + "-params"], listSpec("string"), []);
+                params?.map(p => this.dataDoc[p] = undefined);
             }, icon: "trash"
         });
 
@@ -64,32 +53,35 @@ export class LabelBox extends DocComponent<FieldViewProps, LabelDocument>(LabelD
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
         const docDragData = de.complete.docDragData;
-        const params = this.Document.buttonParams;
-        const missingParams = params?.filter(p => this.props.Document[p] === undefined);
+        const params = Cast(this.dataDoc[this.fieldKey + "-params"], listSpec("string"), []);
+        const missingParams = params?.filter(p => this.dataDoc[p] === undefined);
         if (docDragData && missingParams?.includes((e.target as any).textContent)) {
-            this.props.Document[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
+            this.dataDoc[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
                 d.onDragStart ? docDragData.draggedDocuments[i] : d));
             e.stopPropagation();
         }
     }
     // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
     render() {
-        const params = this.Document.buttonParams;
-        const missingParams = params?.filter(p => this.props.Document[p] === undefined);
-        params?.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ... 
+        const params = Cast(this.dataDoc[this.fieldKey + "-params"], listSpec("string"), []);
+        const missingParams = params?.filter(p => this.dataDoc[p] === undefined);
+        params?.map(p => DocListCast(this.dataDoc[p])); // bcz: really hacky form of prefetching ... 
         return (
             <div className="labelBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
-                style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}>
+                style={{ boxShadow: this.layoutDoc.opacity ? StrCast(this.layoutDoc.boxShadow) : "" }}>
                 <div className="labelBox-mainButton" style={{
-                    background: this.Document.backgroundColor, color: this.Document.color || "inherit",
-                    fontSize: this.Document.fontSize, letterSpacing: this.Document.letterSpacing || "", textTransform: (this.Document.textTransform as any) || ""
+                    background: StrCast(this.layoutDoc.backgroundColor),
+                    color: StrCast(this.layoutDoc.color, "inherit"),
+                    fontSize: NumCast(this.layoutDoc.fontSize) || "inherit",
+                    letterSpacing: StrCast(this.layoutDoc.letterSpacing),
+                    textTransform: StrCast(this.layoutDoc.textTransform) as any
                 }} >
                     <div className="labelBox-mainButtonCenter">
-                        {(this.Document.text || this.Document.title)}
+                        {StrCast(this.layoutDoc.text, StrCast(this.layoutDoc.title))}
                     </div>
                 </div>
                 <div className="labelBox-params" >
-                    {!missingParams || !missingParams.length ? (null) : missingParams.map(m => <div key={m} className="labelBox-missingParam">{m}</div>)}
+                    {!missingParams?.length ? (null) : missingParams.map(m => <div key={m} className="labelBox-missingParam">{m}</div>)}
                 </div>
             </div>
         );
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index f3e099dd6..13ffc6956 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -7,7 +7,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { Utils, setupMoveUpEvents } from '../../../Utils';
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
 import "./LinkAnchorBox.scss";
 import { FieldView, FieldViewProps } from "./FieldView";
 import React = require("react");
@@ -25,7 +25,7 @@ type LinkAnchorSchema = makeInterface<[typeof documentSchema]>;
 const LinkAnchorDocument = makeInterface(documentSchema);
 
 @observer
-export class LinkAnchorBox extends DocExtendableComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); }
     _doubleTap = false;
     _lastTap: number = 0;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 542c86049..af4bf420f 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -4,7 +4,7 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
 import { makeInterface, listSpec } from "../../../new_fields/Schema";
 import { returnFalse, returnZero } from "../../../Utils";
 import { CollectionTreeView } from "../collections/CollectionTreeView";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./LinkBox.scss";
 import { Cast } from "../../../new_fields/Types";
@@ -13,7 +13,7 @@ type LinkDocument = makeInterface<[typeof documentSchema]>;
 const LinkDocument = makeInterface(documentSchema);
 
 @observer
-export class LinkBox extends DocExtendableComponent<FieldViewProps, LinkDocument>(LinkDocument) {
+export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(LinkDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
     render() {
         return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index f2a3e1484..6db36e43c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -13,7 +13,7 @@ import { undoBatch } from '../../util/UndoManager';
 import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { PDFViewer } from "../pdf/PDFViewer";
 import { FieldView, FieldViewProps } from './FieldView';
 import { pageSchema } from "./ImageBox";
@@ -26,7 +26,7 @@ type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, t
 const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
 
 @observer
-export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>(PdfDocument) {
+export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocument>(PdfDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); }
     private _keyValue: string = "";
     private _valueValue: string = "";
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index bea3170ac..e428e16da 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -16,7 +16,7 @@ import { CollectionView, CollectionViewType } from "../collections/CollectionVie
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./PresBox.scss";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
 import { makeInterface } from "../../../new_fields/Schema";
 
 library.add(faArrowLeft);
@@ -33,7 +33,7 @@ type PresBoxSchema = makeInterface<[typeof documentSchema]>;
 const PresBoxDocument = makeInterface(documentSchema);
 
 @observer
-export class PresBox extends DocExtendableComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
+export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
     _childReaction: IReactionDisposer | undefined;
     @observable _isChildActive = false;
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
index 7016b4f04..947167200 100644
--- a/src/client/views/nodes/QueryBox.tsx
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -6,7 +6,7 @@ import { Id } from '../../../new_fields/FieldSymbols';
 import { makeInterface } from "../../../new_fields/Schema";
 import { StrCast } from "../../../new_fields/Types";
 import { SelectionManager } from "../../util/SelectionManager";
-import { DocAnnotatableComponent } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
 import { SearchBox } from "../search/SearchBox";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./QueryBox.scss";
@@ -15,7 +15,7 @@ type QueryDocument = makeInterface<[typeof documentSchema]>;
 const QueryDocument = makeInterface(documentSchema);
 
 @observer
-export class QueryBox extends DocAnnotatableComponent<FieldViewProps, QueryDocument>(QueryDocument) {
+export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryDocument>(QueryDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(QueryBox, fieldKey); }
     _docListChangedReaction: IReactionDisposer | undefined;
     componentDidMount() {
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 1a991f1eb..11b24b059 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -14,7 +14,7 @@ import { Docs, DocUtils } from "../../documents/Documents";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
-import { DocComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ScreenshotBox.scss";
@@ -26,7 +26,7 @@ const ScreenshotDocument = makeInterface(documentSchema, positionSchema);
 library.add(faVideo);
 
 @observer
-export class ScreenshotBox extends DocComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
+export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
     private _reactionDisposer?: IReactionDisposer;
     private _videoRef: HTMLVideoElement | null = null;
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
@@ -109,7 +109,7 @@ export class ScreenshotBox extends DocComponent<FieldViewProps, ScreenshotDocume
     }
     @observable _screenCapture = false;
     specificContextMenu = (e: React.MouseEvent): void => {
-        const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+        const field = Cast(this.dataDoc[this.fieldKey], VideoField);
         if (field) {
             const url = field.url.href;
             const subitems: ContextMenuProps[] = [];
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 309ee3620..70f29e1dc 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -7,7 +7,7 @@ import { ScriptField } from "../../../new_fields/ScriptField";
 import { StrCast, ScriptCast, Cast } from "../../../new_fields/Types";
 import { InteractionUtils } from "../../util/InteractionUtils";
 import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { EditableView } from "../EditableView";
 import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import "./ScriptingBox.scss";
@@ -20,7 +20,7 @@ type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentS
 const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema);
 
 @observer
-export class ScriptingBox extends DocAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) {
+export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) {
     protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); }
 
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 844d95d11..746ea0b64 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -1,22 +1,20 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faEdit } from '@fortawesome/free-regular-svg-icons';
-import { computed, runInAction } from 'mobx';
+import { runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Handles, Rail, Slider, Tracks, Ticks } from 'react-compound-slider';
-import { Doc } from '../../../new_fields/Doc';
+import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
 import { documentSchema } from '../../../new_fields/documentSchemas';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
+import { createSchema, makeInterface } from '../../../new_fields/Schema';
 import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, FieldValue, StrCast, NumCast, Cast } from '../../../new_fields/Types';
-import { DragManager } from '../../util/DragManager';
+import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocComponent } from '../DocComponent';
-import './SliderBox.scss';
-import { Handle, TooltipRail, Track, Tick } from './SliderBox-components';
-import { FieldView, FieldViewProps } from './FieldView';
+import { ViewBoxBaseComponent } from '../DocComponent';
 import { ScriptBox } from '../ScriptBox';
+import { FieldView, FieldViewProps } from './FieldView';
+import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
+import './SliderBox.scss';
 
 
 library.add(faEdit as any);
@@ -32,36 +30,33 @@ type SliderDocument = makeInterface<[typeof SliderSchema, typeof documentSchema]
 const SliderDocument = makeInterface(SliderSchema, documentSchema);
 
 @observer
-export class SliderBox extends DocComponent<FieldViewProps, SliderDocument>(SliderDocument) {
+export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocument>(SliderDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); }
-    private dropDisposer?: DragManager.DragDropDisposer;
-
-    @computed get dataDoc() {
-        return this.props.DataDoc &&
-            (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) ||
-                this.props.DataDoc.layout === this.Document) ? this.props.DataDoc : Doc.GetProto(this.Document);
-    }
 
+    get minThumbKey() { return this.fieldKey + "-minThumb"; }
+    get maxThumbKey() { return this.fieldKey + "-maxThumb"; }
+    get minKey() { return this.fieldKey + "-min"; }
+    get maxKey() { return this.fieldKey + "-max"; }
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
         funcs.push({ description: "Edit Thumb Change Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Thumb Change ...", this.props.Document, "onThumbChange", obj.x, obj.y) });
         ContextMenu.Instance.addItem({ description: "Slider Funcs...", subitems: funcs, icon: "asterisk" });
     }
     onChange = (values: readonly number[]) => runInAction(() => {
-        this.Document._sliderMinThumb = values[0];
-        this.Document._sliderMaxThumb = values[1];
-        Cast(this.Document.onThumbChanged, ScriptField, null)?.script.run({ range: values, this: this.props.Document });
+        this.dataDoc[this.minThumbKey] = values[0];
+        this.dataDoc[this.maxThumbKey] = values[1];
+        Cast(this.layoutDoc.onThumbChanged, ScriptField, null)?.script.run({ self: this.rootDoc, range: values, this: this.layoutDoc });
     })
 
     render() {
-        const domain = [NumCast(this.props.Document._sliderMin), NumCast(this.props.Document._sliderMax)];
-        const defaultValues = [NumCast(this.props.Document._sliderMinThumb), NumCast(this.props.Document._sliderMaxThumb)];
-        return (
+        const domain = [NumCast(this.layoutDoc[this.minKey]), NumCast(this.layoutDoc[this.maxKey])];
+        const defaultValues = [NumCast(this.dataDoc[this.minThumbKey]), NumCast(this.dataDoc[this.maxThumbKey])];
+        return domain[1] <= domain[0] ? (null) : (
             <div className="sliderBox-outerDiv" onContextMenu={this.specificContextMenu} onPointerDown={e => e.stopPropagation()}
-                style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}>
+                style={{ boxShadow: this.layoutDoc.opacity === 0 ? undefined : StrCast(this.layoutDoc.boxShadow, "") }}>
                 <div className="sliderBox-mainButton" onContextMenu={this.specificContextMenu} style={{
-                    background: this.Document.backgroundColor, color: this.Document.color || "black",
-                    fontSize: this.Document.fontSize, letterSpacing: this.Document.letterSpacing || ""
+                    background: StrCast(this.layoutDoc.backgroundColor), color: StrCast(this.layoutDoc.color, "black"),
+                    fontSize: NumCast(this.layoutDoc.fontSize), letterSpacing: StrCast(this.layoutDoc.letterSpacing)
                 }} >
                     <Slider
                         mode={2}
@@ -77,7 +72,7 @@ export class SliderBox extends DocComponent<FieldViewProps, SliderDocument>(Slid
                             {({ handles, activeHandleID, getHandleProps }) => (
                                 <div className="slider-handles">
                                     {handles.map((handle, i) => {
-                                        const value = i === 0 ? this.Document._sliderMinThumb : this.Document._sliderMaxThumb;
+                                        const value = i === 0 ? defaultValues[0] : defaultValues[1];
                                         return (
                                             <div title={String(value)}>
                                                 <Handle
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 2c67883e9..588068334 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -16,7 +16,7 @@ import { Docs, DocUtils } from "../../documents/Documents";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
@@ -33,7 +33,7 @@ const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema);
 library.add(faVideo);
 
 @observer
-export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
+export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
     static _youtubeIframeCounter: number = 0;
     private _reactionDisposer?: IReactionDisposer;
     private _youtubeReactionDisposer?: IReactionDisposer;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index ea5d601ec..55ad7eb0f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -14,7 +14,7 @@ import { Docs } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 import { SelectionManager } from "../../util/SelectionManager";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { InkingControl } from "../InkingControl";
 import { FieldView, FieldViewProps } from './FieldView';
@@ -33,7 +33,7 @@ type WebDocument = makeInterface<[typeof documentSchema]>;
 const WebDocument = makeInterface(documentSchema);
 
 @observer
-export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
+export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
 
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
     @observable private collapsed: boolean = true;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f93c1fa97..c49e6512a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,7 +23,7 @@ import Annotation from "./Annotation";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { SelectionManager } from "../../util/SelectionManager";
 import { undoBatch } from "../../util/UndoManager";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { documentSchema } from "../../../new_fields/documentSchemas";
 import { DocumentDecorations } from "../DocumentDecorations";
@@ -79,7 +79,7 @@ interface IViewerProps {
  * Handles rendering and virtualization of the pdf
  */
 @observer
-export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument>(PdfDocument) {
+export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocument>(PdfDocument) {
     static _annotationStyle: any = addStyleSheet();
     @observable private _pageSizes: { width: number, height: number }[] = [];
     @observable private _annotations: Doc[] = [];
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index bed181ab9..dd0cbf929 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -12,7 +12,7 @@ import { Cast, NumCast } from "../../../new_fields/Types";
 import { emptyFunction, emptyPath, returnFalse, returnTrue } from "../../../Utils";
 import { Transform } from "../../util/Transform";
 import { CollectionViewType } from '../collections/CollectionView';
-import { DocExtendableComponent } from '../DocComponent';
+import { ViewBoxBaseComponent } from '../DocComponent';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import "./PresElementBox.scss";
@@ -44,7 +44,7 @@ const PresDocument = makeInterface(presSchema, documentSchema);
  * It involves some functionality for its buttons and options.
  */
 @observer
-export class PresElementBox extends DocExtendableComponent<FieldViewProps, PresDocument>(PresDocument) {
+export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDocument>(PresDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
 
     _heightDisposer: IReactionDisposer | undefined;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index a9c97fc19..934ba0f60 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -645,11 +645,12 @@ export namespace Doc {
             Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
             (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue));
         }
-        if (templateCaptionValue instanceof RichTextField && (templateCaptionValue.Text || templateCaptionValue.Data.toString().includes("dashField"))) {
-            templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`, { this: Doc.name });
+        // copy the textTemplates from 'this' (not 'self') because the layout contains the template info, not the original doc
+        if (templateCaptionValue instanceof RichTextField && !templateCaptionValue.Empty()) {
+            templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`);
         }
-        if (templateFieldValue instanceof RichTextField && (templateFieldValue.Text || templateFieldValue.Data.toString().includes("dashField"))) {
-            templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`, { this: Doc.name });
+        if (templateFieldValue instanceof RichTextField && !templateFieldValue.Empty()) {
+            templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`);
         }
 
         // get the layout string that the template uses to specify its layout
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index a5a81f4a4..5cf0e0cc3 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -19,6 +19,10 @@ export class RichTextField extends ObjectField {
         this.Text = text;
     }
 
+    Empty() {
+        return !(this.Text || this.Data.toString().includes("dashField"));
+    }
+
     [Copy]() {
         return new RichTextField(this.Data, this.Text);
     }
-- 
cgit v1.2.3-70-g09d2

