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/views/nodes/PresBox.tsx | 110 ++++++++++++++++++++-----------------
 1 file changed, 60 insertions(+), 50 deletions(-)

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

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/PresBox.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

