From e262d9ac73af5b2cef384468c47d69917e205d44 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 7 Apr 2020 23:01:46 -0400
Subject: lots of code cleanup - removed all northstar db stuff.  added
 scriptingBox.  renamed things.  made collectiontypes strings not numbers.

---
 src/client/views/nodes/DocumentContentsView.tsx | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index dc71ba280..7522af3a3 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -3,7 +3,6 @@ import { observer } from "mobx-react";
 import { Doc, Opt } from "../../../new_fields/Doc";
 import { Cast, StrCast } from "../../../new_fields/Types";
 import { OmitKeys, Without } from "../../../Utils";
-import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
 import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -11,10 +10,11 @@ import { CollectionSchemaView } from "../collections/CollectionSchemaView";
 import { CollectionView } from "../collections/CollectionView";
 import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
 import { AudioBox } from "./AudioBox";
-import { ButtonBox } from "./ButtonBox";
+import { LabelBox } from "./LabelBox";
 import { SliderBox } from "./SliderBox";
 import { LinkBox } from "./LinkBox";
-import { DocumentBox } from "./DocumentBox";
+import { ScriptingBox } from "./ScriptingBox";
+import { DocHolderBox } from "./DocumentBox";
 import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import { FontIconBox } from "./FontIconBox";
@@ -27,7 +27,7 @@ import { PresBox } from "./PresBox";
 import { QueryBox } from "./QueryBox";
 import { ColorBox } from "./ColorBox";
 import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
-import { DocuLinkBox } from "./DocuLinkBox";
+import { LinkAnchorBox } from "./LinkAnchorBox";
 import { PresElementBox } from "../presentationview/PresElementBox";
 import { ScreenshotBox } from "./ScreenshotBox";
 import { VideoBox } from "./VideoBox";
@@ -109,10 +109,10 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                 <ObserverJsxParser
                     blacklistedAttrs={[]}
                     components={{
-                        FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, ButtonBox, SliderBox, FieldView,
+                        FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
                         CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                        PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
-                        ColorBox, DashWebRTCVideo, DocuLinkBox, InkingStroke, DocumentBox, LinkBox,
+                        PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
+                        ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
                         RecommendationsBox, ScreenshotBox
                     }}
                     bindings={this.CreateBindings()}
-- 
cgit v1.2.3-70-g09d2


From 3d629495644631e6b614b1b48f1d32d6c7aeefc6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 Apr 2020 17:25:17 -0400
Subject: added parameterization capability to layout strings.  added Div's
 that can take Dash field values

---
 src/client/util/RichTextSchema.tsx              |  6 ++--
 src/client/views/DocumentDecorations.tsx        |  4 +--
 src/client/views/collections/CollectionView.tsx |  7 ++--
 src/client/views/nodes/DocumentContentsView.tsx | 45 +++++++++++++++++++++----
 4 files changed, 46 insertions(+), 16 deletions(-)

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

diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 1522f5e21..d23962d5c 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -852,9 +852,9 @@ export class DashDocView {
             };
             this._renderDisposer?.();
             this._renderDisposer = reaction(() => {
-                if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
-                    finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here?
-                }
+                // if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
+                //     finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here?
+                // }
                 const layoutKey = StrCast(finalLayout.layoutKey);
                 const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1];
                 if (finalLayout !== dashDoc && finalKey) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c8766a6b3..bd41788d4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -3,7 +3,7 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DataSym } from "../../new_fields/Doc";
+import { Doc, DataSym, Field } from "../../new_fields/Doc";
 import { PositionDocument } from '../../new_fields/documentSchemas';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
@@ -366,7 +366,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 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-";
+                return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
             }
             return this._accumulatedTitle;
         } else if (SelectionManager.SelectedDocuments().length > 1) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 178c3189b..4448fcf21 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -211,11 +211,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
         subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
         subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
         subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
-        if (addExtras) switch (this.props.Document._viewType) {
-            case CollectionViewType.Freeform: {
-                subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
-                break;
-            }
+        if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) {
+            subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
         }
         addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
         !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" });
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 7522af3a3..765416658 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,8 +1,8 @@
 import { computed } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../new_fields/Doc";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { OmitKeys, Without } from "../../../Utils";
+import { Doc, Opt, Field } from "../../../new_fields/Doc";
+import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { OmitKeys, Without, emptyPath } from "../../../Utils";
 import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -53,6 +53,31 @@ class ObserverJsxParser1 extends JsxParser {
 
 const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
+
+interface DivProps {
+    Document: Doc;
+}
+
+@observer
+export class Div extends React.Component<DivProps> {
+    render() {
+        const style: { [key: string]: any } = {};
+        const divKeys = OmitKeys(this.props, ["children", "Document", "key", "__proto__"]).omit;
+        Object.keys(divKeys).map((prop: string) => {
+            let p = (this.props as any)[prop] as string;
+            if (p?.startsWith("@")) {  // bcz: extend this to support expression -- is this really a script?
+                const key = p.substring(1);
+                const n = Cast(this.props.Document[key], "number", null);
+                p = n ? n.toString() : StrCast(this.props.Document[key], p);
+            }
+            style[prop] = p;
+        });
+        return <div style={style}>
+            {this.props.children}
+        </div>;
+    }
+}
+
 @observer
 export class DocumentContentsView extends React.Component<DocumentViewProps & {
     isSelected: (outsideReaction: boolean) => boolean,
@@ -102,21 +127,29 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
     render() {
         TraceMobx();
-        return (this.props.renderDepth > 12 || !this.layout || !this.layoutDoc) ? (null) :
+        let layoutFrame = this.layout;
+        const replacer = (match: any, p1: string, offset: any, string: any) => {
+            const n = Cast(this.props.Document[p1], "number", null);
+            return n !== undefined ? n.toString() : StrCast(this.props.Document[p1], p1);
+        };
+        layoutFrame = layoutFrame.replace(/\{([a-zA-Z0-9_-]+)\}/g, replacer);
+        return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
             this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ?
                 <FormattedTextBox {...this.CreateBindings().props} fieldKey={this.props.forceFieldKey} />
                 :
                 <ObserverJsxParser
+                    key={42}
                     blacklistedAttrs={[]}
+                    renderInWrapper={false}
                     components={{
                         FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
                         CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
                         PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
                         ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
-                        RecommendationsBox, ScreenshotBox
+                        RecommendationsBox, ScreenshotBox, Div
                     }}
                     bindings={this.CreateBindings()}
-                    jsx={this.layout}
+                    jsx={layoutFrame}
                     showWarnings={true}
 
                     onError={(test: any) => { console.log(test); }}
-- 
cgit v1.2.3-70-g09d2


From 1808f23e8e72cfca8517d3788f9f89ec989f1062 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 Apr 2020 22:09:18 -0400
Subject: functional version of html tags in layout strings including scripts.

---
 src/client/views/nodes/DocumentContentsView.tsx | 58 ++++++++++++++++++-------
 1 file changed, 42 insertions(+), 16 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 765416658..d577068b7 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -37,6 +37,8 @@ import React = require("react");
 import { RecommendationsBox } from "../RecommendationsBox";
 
 import { TraceMobx } from "../../../new_fields/util";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import ReactTable from "react-table";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -54,27 +56,38 @@ class ObserverJsxParser1 extends JsxParser {
 const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
 
-interface DivProps {
+interface HTMLtagProps {
     Document: Doc;
+    htmltag: string;
 }
-
+//"<HTMLdiv borderRadius='100px' onClick={() => this.bannerColor=this.bannerColor==='red'?'green':'red'} width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)' backgroundColor='{this.bannerColor}'><ImageBox {...props} fieldKey={'data'}/><HTMLspan width='100%'  marginTop='50%'  height='10%'  position='absolute' backgroundColor='green'>{this.title}</HTMLspan></HTMLdiv>"
 @observer
-export class Div extends React.Component<DivProps> {
+export class HTMLtag extends React.Component<HTMLtagProps> {
+    click = (e: React.MouseEvent) => {
+        const clickScript = (this.props as any).onClick;
+        ScriptField.MakeScript(clickScript, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document });
+    }
     render() {
         const style: { [key: string]: any } = {};
-        const divKeys = OmitKeys(this.props, ["children", "Document", "key", "__proto__"]).omit;
+        const divKeys = OmitKeys(this.props, ["children", "htmltag", "Document", "key", "onClick", "__proto__"]).omit;
         Object.keys(divKeys).map((prop: string) => {
             let p = (this.props as any)[prop] as string;
-            if (p?.startsWith("@")) {  // bcz: extend this to support expression -- is this really a script?
-                const key = p.substring(1);
+            const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: extend this to support expression -- is this really a script?
+                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "";
+            };
+            p = p?.replace(/{([^.'][^}']+)}/g, replacer);
+
+            const replacer2 = (match: any, key: string, offset: any, string: any) => { // bcz: extend this to support expression -- is this really a script?
                 const n = Cast(this.props.Document[key], "number", null);
-                p = n ? n.toString() : StrCast(this.props.Document[key], p);
-            }
-            style[prop] = p;
+                return n ? n.toString() : StrCast(this.props.Document[key], p);
+            };
+            style[prop] = p?.replace(/@([a-zA-Z0-9-_]+)/g, replacer2);
+
         });
-        return <div style={style}>
+        const Tag = this.props.htmltag as keyof JSX.IntrinsicElements;
+        return <Tag style={style} onClick={this.click}>
             {this.props.children}
-        </div>;
+        </Tag>;
     }
 }
 
@@ -128,11 +141,24 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     render() {
         TraceMobx();
         let layoutFrame = this.layout;
-        const replacer = (match: any, p1: string, offset: any, string: any) => {
-            const n = Cast(this.props.Document[p1], "number", null);
-            return n !== undefined ? n.toString() : StrCast(this.props.Document[p1], p1);
+        // replace code content with a script  >{content}<   as in  <HTMLdiv>{this.title}</HTMLdiv>
+        const replacer = (match: any, expr: string, offset: any, string: any) => {
+            return ">" + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + "<";
         };
-        layoutFrame = layoutFrame.replace(/\{([a-zA-Z0-9_-]+)\}/g, replacer);
+        layoutFrame = layoutFrame.replace(/>\{([^.'][^<}]+)\}</g, replacer);
+
+        // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'> 
+        const replacer2 = (match: any, p1: string, offset: any, string: any) => {
+            return `<HTMLtag Document={props.Document} htmltag='${p1}'`;
+        };
+        layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
+
+        // replace /HTML<tag> with </HTMLdiv>  as in:  </HTMLdiv> becomes  </HTMLtag> 
+        const replacer3 = (match: any, p1: string, offset: any, string: any) => {
+            return `</HTMLtag`;
+        };
+        layoutFrame = layoutFrame.replace(/<\/HTML([a-zA-Z0-9_-]+)/g, replacer3);
+
         return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
             this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ?
                 <FormattedTextBox {...this.CreateBindings().props} fieldKey={this.props.forceFieldKey} />
@@ -146,7 +172,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                         CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
                         PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
                         ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
-                        RecommendationsBox, ScreenshotBox, Div
+                        RecommendationsBox, ScreenshotBox, HTMLtag
                     }}
                     bindings={this.CreateBindings()}
                     jsx={layoutFrame}
-- 
cgit v1.2.3-70-g09d2


From 682782a1337003de1694d1625d262a1efddcb02d Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 Apr 2020 23:50:20 -0400
Subject: tweaked HTMLtag syntax slightly for onClick functions to be specified
 without quotes

---
 package-lock.json                                  | 27 ++++++++++++++++++
 package.json                                       |  2 ++
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 src/client/views/nodes/DocumentContentsView.tsx    | 32 ++++++++++++++++------
 src/client/views/search/SearchBox.tsx              |  4 +--
 5 files changed, 54 insertions(+), 12 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 940321132..176d1f27e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -43,6 +43,15 @@
                 "regenerator-runtime": "^0.13.4"
             }
         },
+        "@babel/runtime-corejs3": {
+            "version": "7.9.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.2.tgz",
+            "integrity": "sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA==",
+            "requires": {
+                "core-js-pure": "^3.0.0",
+                "regenerator-runtime": "^0.13.4"
+            }
+        },
         "@babel/types": {
             "version": "7.9.5",
             "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
@@ -1194,6 +1203,11 @@
             "resolved": "https://registry.npmjs.org/@types/word-extractor/-/word-extractor-0.3.0.tgz",
             "integrity": "sha512-XcVsLXMpPT6Lv4Qvsc2yPoWg3zPVKEIyQ21eQ1ka58zZjtd7vUZ2kx0KydRbsNRNGopEIiwlemGzgo46dytR2Q=="
         },
+        "@types/xregexp": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/@types/xregexp/-/xregexp-4.3.0.tgz",
+            "integrity": "sha512-3gJTS9gt27pS7U9q5IVqo4YvKSlkf2ck8ish6etuDj6LIRxkL/2Y8RMUtK/QzvE1Yv2zwWV5yemI2BS0GGGFnA=="
+        },
         "@types/yargs": {
             "version": "13.0.8",
             "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz",
@@ -3456,6 +3470,11 @@
             "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
             "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
         },
+        "core-js-pure": {
+            "version": "3.6.5",
+            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
+            "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
+        },
         "core-util-is": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -18092,6 +18111,14 @@
             "resolved": "https://registry.npmjs.org/xoauth2/-/xoauth2-1.2.0.tgz",
             "integrity": "sha1-8u76wRRyyXHqO8RuVU60sSMhRuU="
         },
+        "xregexp": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz",
+            "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==",
+            "requires": {
+                "@babel/runtime-corejs3": "^7.8.3"
+            }
+        },
         "xtend": {
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/package.json b/package.json
index ed846fcff..f1b4be520 100644
--- a/package.json
+++ b/package.json
@@ -127,6 +127,7 @@
         "@types/uuid": "^3.4.6",
         "@types/webpack": "^4.41.3",
         "@types/word-extractor": "^0.3.0",
+        "@types/xregexp": "^4.3.0",
         "@types/youtube": "0.0.38",
         "adm-zip": "^0.4.13",
         "archiver": "^3.1.1",
@@ -258,6 +259,7 @@
         "word-extractor": "^0.3.0",
         "words-to-numbers": "^1.5.1",
         "xoauth2": "^1.2.0",
+        "xregexp": "^4.3.0",
         "youtube": "^0.1.0"
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d2106808e..3b5101a4d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,7 +44,6 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { CollectionViewType } from "../CollectionView";
-import { Script } from "vm";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index d577068b7..b5af68ba1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -35,10 +35,10 @@ import { WebBox } from "./WebBox";
 import { InkingStroke } from "../InkingStroke";
 import React = require("react");
 import { RecommendationsBox } from "../RecommendationsBox";
-
 import { TraceMobx } from "../../../new_fields/util";
 import { ScriptField } from "../../../new_fields/ScriptField";
-import ReactTable from "react-table";
+import XRegExp = require("xregexp");
+
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -59,13 +59,13 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 interface HTMLtagProps {
     Document: Doc;
     htmltag: string;
+    onClick?: ScriptField;
 }
-//"<HTMLdiv borderRadius='100px' onClick={() => this.bannerColor=this.bannerColor==='red'?'green':'red'} width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)' backgroundColor='{this.bannerColor}'><ImageBox {...props} fieldKey={'data'}/><HTMLspan width='100%'  marginTop='50%'  height='10%'  position='absolute' backgroundColor='green'>{this.title}</HTMLspan></HTMLdiv>"
-@observer
+//"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'><ImageBox {...props} fieldKey={'data'}/><HTMLspan width='100%'  marginTop='50%'  height='10%'  position='absolute' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}grey'>{this.title}</HTMLspan></HTMLdiv>"@observer
 export class HTMLtag extends React.Component<HTMLtagProps> {
     click = (e: React.MouseEvent) => {
-        const clickScript = (this.props as any).onClick;
-        ScriptField.MakeScript(clickScript, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document });
+        const clickScript = (this.props as any).onClick as Opt<ScriptField>;
+        clickScript?.script.run({ this: this.props.Document });
     }
     render() {
         const style: { [key: string]: any } = {};
@@ -129,11 +129,12 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         return Doc.expandTemplateLayout(template, this.props.Document, params ? "(" + params + ")" : this.props.layoutKey);
     }
 
-    CreateBindings(): JsxBindings {
+    CreateBindings(onClick: Opt<ScriptField>): JsxBindings {
         const list = {
             ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
             Document: this.layoutDoc,
             DataDoc: this.dataDoc,
+            onClick: onClick
         };
         return { props: list };
     }
@@ -141,6 +142,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     render() {
         TraceMobx();
         let layoutFrame = this.layout;
+
         // replace code content with a script  >{content}<   as in  <HTMLdiv>{this.title}</HTMLdiv>
         const replacer = (match: any, expr: string, offset: any, string: any) => {
             return ">" + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + "<";
@@ -159,9 +161,21 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         };
         layoutFrame = layoutFrame.replace(/<\/HTML([a-zA-Z0-9_-]+)/g, replacer3);
 
+        // add onClick function to props
+        const splits = layoutFrame.split("onClick=");
+        let onClick: Opt<ScriptField>;
+        if (splits.length > 1) {
+            const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
+            layoutFrame = splits[0] + " onClick={props.onClick} " + splits[1].substring(code[1].end + 1);
+            onClick = ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name });
+        }
+
+        const bindings = this.CreateBindings(onClick);
+        //  layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
+
         return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
             this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ?
-                <FormattedTextBox {...this.CreateBindings().props} fieldKey={this.props.forceFieldKey} />
+                <FormattedTextBox {...bindings.props} fieldKey={this.props.forceFieldKey} />
                 :
                 <ObserverJsxParser
                     key={42}
@@ -174,7 +188,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                         ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
                         RecommendationsBox, ScreenshotBox, HTMLtag
                     }}
-                    bindings={this.CreateBindings()}
+                    bindings={bindings}
                     jsx={layoutFrame}
                     showWarnings={true}
 
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index b0a49f750..e41b725b1 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -29,7 +29,7 @@ export interface SearchProps {
     filterQquery?: string;
     setSearchQuery: (q: string) => {};
     searchFileTypes: string[];
-    setSearchFileTypes: (types: string[]) => {}
+    setSearchFileTypes: (types: string[]) => {};
 }
 
 export enum Keys {
@@ -86,7 +86,7 @@ export class SearchBox extends React.Component<SearchProps> {
             this._searchString = this.props.searchQuery;
             this.submitSearch();
         }
-    })
+    });
 
 
     @action
-- 
cgit v1.2.3-70-g09d2


From f8019f3894cfcff29318a95d8c71a033de7a2ac6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 22 Apr 2020 00:20:10 -0400
Subject: fixed div content scripts

---
 src/client/views/nodes/DocumentContentsView.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index b5af68ba1..bc8bf3ba2 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -144,10 +144,10 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         let layoutFrame = this.layout;
 
         // replace code content with a script  >{content}<   as in  <HTMLdiv>{this.title}</HTMLdiv>
-        const replacer = (match: any, expr: string, offset: any, string: any) => {
-            return ">" + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + "<";
+        const replacer = (match: any, prefix: string, expr: string, postfix: string, offset: any, string: any) => {
+            return prefix + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + postfix;
         };
-        layoutFrame = layoutFrame.replace(/>\{([^.'][^<}]+)\}</g, replacer);
+        layoutFrame = layoutFrame.replace(/(>[^{]*)\{([^.'][^<}]+)\}([^}]*<)/g, replacer);
 
         // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'> 
         const replacer2 = (match: any, p1: string, offset: any, string: any) => {
-- 
cgit v1.2.3-70-g09d2


From 3a118ee726355197bb37910785e69d75033c2f43 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 22 Apr 2020 00:22:34 -0400
Subject: oops.

---
 src/client/views/nodes/DocumentContentsView.tsx | 1 +
 1 file changed, 1 insertion(+)

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

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index bc8bf3ba2..a022f2e02 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -62,6 +62,7 @@ interface HTMLtagProps {
     onClick?: ScriptField;
 }
 //"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'><ImageBox {...props} fieldKey={'data'}/><HTMLspan width='100%'  marginTop='50%'  height='10%'  position='absolute' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}grey'>{this.title}</HTMLspan></HTMLdiv>"@observer
+@observer
 export class HTMLtag extends React.Component<HTMLtagProps> {
     click = (e: React.MouseEvent) => {
         const clickScript = (this.props as any).onClick as Opt<ScriptField>;
-- 
cgit v1.2.3-70-g09d2


From 11bb2c9b37f5c08639fcbb1ed451137188765189 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 26 Apr 2020 21:34:42 -0400
Subject: from last

---
 src/client/views/nodes/DocumentContentsView.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index a022f2e02..812b17d64 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -126,7 +126,9 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
     get layoutDoc() {
         const params = StrCast(this.props.Document.PARAMS);
-        const template: Doc = this.props.LayoutDoc?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+        // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
+        // const template: Doc = this.props.LayoutDoc?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+        const template: Doc = this.props.LayoutDoc?.() ||
         return Doc.expandTemplateLayout(template, this.props.Document, params ? "(" + params + ")" : this.props.layoutKey);
     }
 
-- 
cgit v1.2.3-70-g09d2


From 20dd1572d6d56cfe09f6488a890791f636d3e336 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 26 Apr 2020 22:41:10 -0400
Subject: from last

---
 src/client/views/nodes/DocumentContentsView.tsx | 57 ++++++++++++++-----------
 1 file changed, 33 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 812b17d64..8582f92ed 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -58,35 +58,34 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
 interface HTMLtagProps {
     Document: Doc;
+    RootDoc: Doc;
     htmltag: string;
     onClick?: ScriptField;
+    onInput?: ScriptField;
 }
 //"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'><ImageBox {...props} fieldKey={'data'}/><HTMLspan width='100%'  marginTop='50%'  height='10%'  position='absolute' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}grey'>{this.title}</HTMLspan></HTMLdiv>"@observer
 @observer
 export class HTMLtag extends React.Component<HTMLtagProps> {
     click = (e: React.MouseEvent) => {
         const clickScript = (this.props as any).onClick as Opt<ScriptField>;
-        clickScript?.script.run({ this: this.props.Document });
+        clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc });
+    }
+    onInput = (e: React.FormEvent<HTMLDivElement>) => {
+        const onInputScript = (this.props as any).onInput as Opt<ScriptField>;
+        onInputScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, value: (e.target as any).textContent });
     }
     render() {
         const style: { [key: string]: any } = {};
-        const divKeys = OmitKeys(this.props, ["children", "htmltag", "Document", "key", "onClick", "__proto__"]).omit;
+        const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit;
         Object.keys(divKeys).map((prop: string) => {
             let p = (this.props as any)[prop] as string;
-            const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: extend this to support expression -- is this really a script?
-                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "";
-            };
-            p = p?.replace(/{([^.'][^}']+)}/g, replacer);
-
-            const replacer2 = (match: any, key: string, offset: any, string: any) => { // bcz: extend this to support expression -- is this really a script?
-                const n = Cast(this.props.Document[key], "number", null);
-                return n ? n.toString() : StrCast(this.props.Document[key], p);
+            const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string:  { script }  into a value
+                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || "";
             };
-            style[prop] = p?.replace(/@([a-zA-Z0-9-_]+)/g, replacer2);
-
+            style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer);
         });
         const Tag = this.props.htmltag as keyof JSX.IntrinsicElements;
-        return <Tag style={style} onClick={this.click}>
+        return <Tag style={style} onClick={this.click} onInput={this.onInput as any}>
             {this.props.children}
         </Tag>;
     }
@@ -105,7 +104,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     @computed get layout(): string {
         TraceMobx();
         if (!this.layoutDoc) return "<p>awaiting layout</p>";
-        const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string");
+        // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string");  // bcz: replaced this with below... is it right?
+        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string");
         if (this.props.layoutKey === "layout_keyValue") {
             return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
         } else
@@ -129,15 +129,19 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
         // const template: Doc = this.props.LayoutDoc?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
         const template: Doc = this.props.LayoutDoc?.() ||
+            (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
+            Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
         return Doc.expandTemplateLayout(template, this.props.Document, params ? "(" + params + ")" : this.props.layoutKey);
     }
 
-    CreateBindings(onClick: Opt<ScriptField>): JsxBindings {
+    CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
         const list = {
             ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
+            RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
             Document: this.layoutDoc,
             DataDoc: this.dataDoc,
-            onClick: onClick
+            onClick: onClick,
+            onInput: onInput
         };
         return { props: list };
     }
@@ -154,7 +158,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
         // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'> 
         const replacer2 = (match: any, p1: string, offset: any, string: any) => {
-            return `<HTMLtag Document={props.Document} htmltag='${p1}'`;
+            return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} htmltag='${p1}'`;
         };
         layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
 
@@ -165,15 +169,20 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         layoutFrame = layoutFrame.replace(/<\/HTML([a-zA-Z0-9_-]+)/g, replacer3);
 
         // add onClick function to props
-        const splits = layoutFrame.split("onClick=");
-        let onClick: Opt<ScriptField>;
-        if (splits.length > 1) {
-            const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
-            layoutFrame = splits[0] + " onClick={props.onClick} " + splits[1].substring(code[1].end + 1);
-            onClick = ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name });
+        const makeFuncProp = (func: string) => {
+            const splits = layoutFrame.split(`func=`);
+            if (splits.length > 1) {
+                const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
+                layoutFrame = splits[0] + ` ${func}={props.onClick} ` + splits[1].substring(code[1].end + 1);
+                return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, value: "string" });
+            }
+            return undefined;
+            // add input function to props
         }
+        let onClick = makeFuncProp("onClick");
+        let onInput = makeFuncProp("onInput");
 
-        const bindings = this.CreateBindings(onClick);
+        const bindings = this.CreateBindings(onClick, onInput);
         //  layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
 
         return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
-- 
cgit v1.2.3-70-g09d2


From fc470b25759e8f051dc527066f9bebcaf5e7707d Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Sun, 26 Apr 2020 23:55:23 -0700
Subject: various buxton fixes

---
 src/client/documents/Documents.ts                  |  1 -
 .../views/collections/CollectionTreeView.tsx       | 24 ++++++++--------
 src/client/views/nodes/DocumentContentsView.tsx    |  8 +++---
 src/client/views/nodes/FormattedTextBox.tsx        |  9 +-----
 src/scraping/buxton/final/BuxtonImporter.ts        | 32 ++++++++++++----------
 5 files changed, 36 insertions(+), 38 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1651a6d55..5e0890e76 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -408,7 +408,6 @@ export namespace Docs {
                     const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true });
                     const deviceProto = Doc.GetProto(doc);
                     deviceProto.hero = new ImageField(constructed[0].url);
-                    deviceProto.fontFamily = "Arial";
                     Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: deviceProto } });
                     Doc.AddDocToList(parentProto, "data", doc);
                 } else if (errors) {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 362d43ee7..dcb5e116c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -733,19 +733,21 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                 const { Document } = this.props;
                 const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
 
+                const carousel = CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" });
                 const textDoc = TextDocument("", { title: "details", _autoHeight: true });
-                const detailView = Docs.Create.StackingDocument([
-                    CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }),
-                    TreeDocument([
-                        // textDoc,
-                        TextDocument("", { title: "shortDescription", _autoHeight: true }),
-                        // TreeDocument([], { title: "narratives", _height: 75, treeViewHideTitle: true }),
-                        TextDocument("", { title: "longDescription", _height: 350 })
-                    ], { title: "stuff", _height: 100 })
-                ], { _chromeStatus: "disabled", _width: 300, _height: 300, _autoHeight: true, title: "detailView" });
-                // const detailView = Cast(Cast(Doc.UserDoc()["template-button-detail"], Doc, null)?.dragFactor, Doc, null);
+                const short = TextDocument("", { title: "shortDescription", _autoHeight: true });
+                const long = TextDocument("", { title: "longDescription", _height: 350 });
+                long.treeViewExpandedView = "layout";
+                const long_wrapper = TreeDocument([long], { title: "Descriptions", _height: 350 });
+
+                // const narratives = TreeDocument([], { title: "narratives", _height: 75, treeViewHideTitle: true }),
+                // const detailView = Cast(Cast(Doc.UserDoc()["template-button-detail"], Doc, null)?.dragFactory, Doc, null);
+
+                textDoc.fontFamily = short.fontFamily = long.fontFamily = carousel.fontFamily = "Arial";
+
+                const detailViewOpts = { _chromeStatus: "disabled", _width: 300, _height: 300, _autoHeight: true, title: "detailView" };
+                const detailView = Docs.Create.StackingDocument([carousel, textDoc, short, long_wrapper], detailViewOpts);
                 detailView.isTemplateDoc = makeTemplate(detailView);
-                detailView.fontFamily = "Arial";
 
                 const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"];
                 const detailedTemplate = {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 8582f92ed..cd78ac7b3 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -78,7 +78,7 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
         const style: { [key: string]: any } = {};
         const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit;
         Object.keys(divKeys).map((prop: string) => {
-            let p = (this.props as any)[prop] as string;
+            const p = (this.props as any)[prop] as string;
             const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string:  { script }  into a value
                 return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || "";
             };
@@ -178,9 +178,9 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
             }
             return undefined;
             // add input function to props
-        }
-        let onClick = makeFuncProp("onClick");
-        let onInput = makeFuncProp("onInput");
+        };
+        const onClick = makeFuncProp("onClick");
+        const onInput = makeFuncProp("onInput");
 
         const bindings = this.CreateBindings(onClick, onInput);
         //  layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 8d4b90c41..d98172823 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -860,15 +860,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             });
             const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field);
             if (startupText) {
-                const paragraphSegments = startupText.split("\n\n");
                 const { state: { tr }, dispatch } = this._editorView;
-                if (paragraphSegments.length) {
-                    for (const paragraph of paragraphSegments) {
-                        dispatch(tr.insertText(paragraph));
-                    }
-                } else {
-                    dispatch(tr.insertText(startupText));
-                }
+                dispatch(tr.insertText(startupText));
             }
         }
 
diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts
index 64b988610..713207a07 100644
--- a/src/scraping/buxton/final/BuxtonImporter.ts
+++ b/src/scraping/buxton/final/BuxtonImporter.ts
@@ -16,7 +16,7 @@ interface DocumentContents {
     hyperlinks: string[];
     captions: string[];
     embeddedFileNames: string[];
-    longDescriptionParagraphs: string[];
+    longDescription: string;
 }
 
 export interface DeviceDocument {
@@ -269,7 +269,7 @@ async function extractFileContents(pathToDocument: string): Promise<DocumentCont
     const paragraphs = document.find('//*[name()="w:p"]').map(node => Utilities.correctSentences(node.text()).transformed!);
     const start = paragraphs.indexOf(paragraphs.find(el => /Bill Buxton[’']s Notes/.test(el))!) + 1;
     const end = paragraphs.indexOf("Device Details");
-    const longDescriptionParagraphs = paragraphs.slice(start, end);
+    const longDescription = paragraphs.slice(start, end).filter(paragraph => paragraph.length).join("\n\n");
 
     const { length } = captionTargets;
     strictEqual(length > 3, true, "No captions written.");
@@ -292,7 +292,7 @@ async function extractFileContents(pathToDocument: string): Promise<DocumentCont
 
     zip.close();
 
-    return { body, longDescriptionParagraphs, imageData, captions, embeddedFileNames, hyperlinks };
+    return { body, longDescription, imageData, captions, embeddedFileNames, hyperlinks };
 }
 
 const imageEntry = /^word\/media\/\w+\.(jpeg|jpg|png|gif)/;
@@ -308,26 +308,30 @@ async function writeImages(zip: any): Promise<ImageData[]> {
     const imageEntries = allEntries.filter(name => imageEntry.test(name));
 
     const imageUrls: ImageData[] = [];
-    for (const mediaPath of imageEntries) {
-        const getImageStream = () => new Promise<Readable>((resolve, reject) => {
-            zip.stream(mediaPath, (error: any, stream: any) => error ? reject(error) : resolve(stream));
-        });
+    const valid: any[] = [];
+
+    const getImageStream = (mediaPath: string) => new Promise<Readable>((resolve, reject) => {
+        zip.stream(mediaPath, (error: any, stream: any) => error ? reject(error) : resolve(stream));
+    });
 
+    for (const mediaPath of imageEntries) {
         const { width, height, type } = await new Promise<Dimensions>(async resolve => {
             const sizeStream = (createImageSizeStream() as PassThrough).on('size', (dimensions: Dimensions) => {
                 readStream.destroy();
                 resolve(dimensions);
             }).on("error", () => readStream.destroy());
-            const readStream = await getImageStream();
+            const readStream = await getImageStream(mediaPath);
             readStream.pipe(sizeStream);
         });
-        if (Math.abs(width - height) < 10) {
-            continue;
+
+        if (Math.abs(width - height) > 10) {
+            valid.push({ width, height, type, mediaPath });
         }
+    }
 
+    for (const { type, width, height, mediaPath } of valid) {
         const generatedFileName = `upload_${Utils.GenerateGuid()}.${type.toLowerCase()}`;
-        await DashUploadUtils.outputResizedImages(getImageStream, generatedFileName, imageDir);
-
+        await DashUploadUtils.outputResizedImages(() => getImageStream(mediaPath), generatedFileName, imageDir);
         imageUrls.push({
             url: `/files/images/buxton/${generatedFileName}`,
             nativeWidth: width,
@@ -339,11 +343,12 @@ async function writeImages(zip: any): Promise<ImageData[]> {
 }
 
 function analyze(fileName: string, contents: DocumentContents): AnalysisResult {
-    const { body, imageData, captions, hyperlinks, embeddedFileNames, longDescriptionParagraphs } = contents;
+    const { body, imageData, captions, hyperlinks, embeddedFileNames, longDescription } = contents;
     const device: any = {
         hyperlinks,
         captions,
         embeddedFileNames,
+        longDescription,
         __images: imageData
     };
     const errors: { [key: string]: string } = { fileName };
@@ -378,7 +383,6 @@ function analyze(fileName: string, contents: DocumentContents): AnalysisResult {
         return { errors };
     }
 
-    device.longDescription = longDescriptionParagraphs.join("\n\n");
     return { device };
 }
 
-- 
cgit v1.2.3-70-g09d2

