From 4e00ff1c268f0c5453b448dd1810e14c47b37969 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 28 Jun 2019 16:27:38 -0400
Subject: test2

---
 src/client/views/nodes/DocumentContentsView.tsx | 3 ++-
 1 file changed, 2 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 0da4888a1..240b21714 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -52,7 +52,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         const layout = Cast(layoutDoc[this.props.layoutKey], "string");
         if (layout === undefined) {
             return this.props.Document.data ?
-                "<FieldView {...props} fieldKey='data' />" :
+                "null" :
+                // "<FieldView {...props} fieldKey='data' />" :
                 KeyValueBox.LayoutString(layoutDoc.proto ? "proto" : "");
         } else if (typeof layout === "string") {
             return layout;
-- 
cgit v1.2.3-70-g09d2


From 8ef971485492e3dc461cd93b2ae89e01b9995741 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 28 Jun 2019 16:57:46 -0400
Subject: undo tests

---
 src/client/views/nodes/DocumentContentsView.tsx | 3 +--
 src/client/views/nodes/KeyValueBox.tsx          | 1 -
 src/client/views/pdf/PDFViewer.tsx              | 3 +--
 src/new_fields/Doc.ts                           | 1 -
 4 files changed, 2 insertions(+), 6 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 240b21714..0da4888a1 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -52,8 +52,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         const layout = Cast(layoutDoc[this.props.layoutKey], "string");
         if (layout === undefined) {
             return this.props.Document.data ?
-                "null" :
-                // "<FieldView {...props} fieldKey='data' />" :
+                "<FieldView {...props} fieldKey='data' />" :
                 KeyValueBox.LayoutString(layoutDoc.proto ? "proto" : "");
         } else if (typeof layout === "string") {
             return layout;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index eee4ec670..0e798d291 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -207,7 +207,6 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     render() {
-        return null;
         let dividerDragger = this.splitPercentage === 0 ? (null) :
             <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
                 <div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 380ba3c45..a645b0041 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
@@ -620,7 +620,6 @@ class Viewer extends React.Component<IViewerProps> {
     }
 
     render() {
-        trace();
         let compiled = this._script;
         return (
             <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 0340806ef..27dcfba08 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -281,7 +281,6 @@ export namespace Doc {
     }
 
     export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) {
-        return templateLayoutDoc;
         let resolvedDataDoc = (templateLayoutDoc !== dataDoc) ? dataDoc : undefined;
         if (!dataDoc || !(templateLayoutDoc && !(Cast(templateLayoutDoc.layout, Doc) instanceof Doc) && resolvedDataDoc && resolvedDataDoc !== templateLayoutDoc)) {
             return templateLayoutDoc;
-- 
cgit v1.2.3-70-g09d2


From 74e4909a77ac143ecdb1d038ad182aae9c710129 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Wed, 3 Jul 2019 22:15:25 -0400
Subject: implemented directory import routine

---
 src/client/documents/Documents.ts                  |  57 ++++++++-
 src/client/util/Import & Export/ImageImporter.tsx  |  67 +++++++++++
 src/client/util/Import & Export/ImportBox.tsx      | 134 +++++++++++++++++++++
 src/client/util/SelectionManager.ts                |   2 +-
 src/client/views/MainView.tsx                      |   3 +
 src/client/views/collections/CollectionSubView.tsx |  46 +------
 src/client/views/nodes/DocumentContentsView.tsx    |   3 +-
 7 files changed, 265 insertions(+), 47 deletions(-)
 create mode 100644 src/client/util/Import & Export/ImageImporter.tsx
 create mode 100644 src/client/util/Import & Export/ImportBox.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7d7a1f02a..5d637dd3a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -36,6 +36,7 @@ import { UndoManager } from "../util/UndoManager";
 import { RouteStore } from "../../server/RouteStore";
 import { LinkManager } from "../util/LinkManager";
 import { DocumentManager } from "../util/DocumentManager";
+import ImportBox from "../util/Import & Export/ImportBox";
 var requestImageSize = require('../util/request-image-size');
 var path = require('path');
 
@@ -51,7 +52,8 @@ export enum DocTypes {
     KVP = "kvp",
     VID = "video",
     AUDIO = "audio",
-    LINK = "link"
+    LINK = "link",
+    IMPORT = "import"
 }
 
 export interface DocumentOptions {
@@ -127,6 +129,7 @@ export namespace Docs {
     let audioProto: Doc;
     let pdfProto: Doc;
     let iconProto: Doc;
+    let importProto: Doc;
     // let linkProto: Doc;
     const textProtoId = "textProto";
     const histoProtoId = "histoProto";
@@ -138,6 +141,7 @@ export namespace Docs {
     const videoProtoId = "videoProto";
     const audioProtoId = "audioProto";
     const iconProtoId = "iconProto";
+    const importProtoId = "importProto";
     // const linkProtoId = "linkProto";
 
     export function initProtos(): Promise<void> {
@@ -152,6 +156,7 @@ export namespace Docs {
             audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype();
             pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype();
             iconProto = fields[iconProtoId] as Doc || CreateIconPrototype();
+            importProto = fields[importProtoId] as Doc || CreateImportPrototype();
         });
     }
 
@@ -174,6 +179,11 @@ export namespace Docs {
         return imageProto;
     }
 
+    function CreateImportPrototype(): Doc {
+        let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
+        return importProto;
+    }
+
     function CreateHistogramPrototype(): Doc {
         let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"),
             { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST });
@@ -261,6 +271,10 @@ export namespace Docs {
         return CreateInstance(audioProto, new AudioField(new URL(url)), options);
     }
 
+    export function DirectoryImportDocument(options: DocumentOptions = {}) {
+        return CreateInstance(importProto, "", options);
+    }
+
     export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) {
         return CreateInstance(histoProto, new HistogramField(histoOp), options);
     }
@@ -333,6 +347,47 @@ export namespace Docs {
         return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
     }
 
+    export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise<Opt<Doc>> {
+        let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
+        if (type.indexOf("image") !== -1) {
+            ctor = Docs.ImageDocument;
+        }
+        if (type.indexOf("video") !== -1) {
+            ctor = Docs.VideoDocument;
+        }
+        if (type.indexOf("audio") !== -1) {
+            ctor = Docs.AudioDocument;
+        }
+        if (type.indexOf("pdf") !== -1) {
+            ctor = Docs.PdfDocument;
+            options.nativeWidth = 1200;
+        }
+        if (type.indexOf("excel") !== -1) {
+            ctor = Docs.DBDocument;
+            options.dropAction = "copy";
+        }
+        if (type.indexOf("html") !== -1) {
+            if (path.includes(window.location.hostname)) {
+                let s = path.split('/');
+                let id = s[s.length - 1];
+                DocServer.GetRefField(id).then(field => {
+                    if (field instanceof Doc) {
+                        let alias = Doc.MakeAlias(field);
+                        alias.x = options.x || 0;
+                        alias.y = options.y || 0;
+                        alias.width = options.width || 300;
+                        alias.height = options.height || options.width || 300;
+                        addDocument && addDocument(alias, false);
+                    }
+                });
+                return undefined;
+            }
+            ctor = Docs.WebDocument;
+            options = { height: options.width, ...options, title: path, nativeWidth: undefined };
+        }
+        return ctor ? ctor(path, options) : undefined;
+    }
+
     export function CaptionDocument(doc: Doc) {
         const captionDoc = Doc.MakeAlias(doc);
         captionDoc.overlayLayout = FixedCaption();
diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx
new file mode 100644
index 000000000..d664f6487
--- /dev/null
+++ b/src/client/util/Import & Export/ImageImporter.tsx	
@@ -0,0 +1,67 @@
+import "fs";
+import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
+import { DocServer } from "../../DocServer";
+import { RouteStore } from "../../../server/RouteStore";
+import { action } from "mobx";
+import { Docs } from "../../documents/Documents";
+import { FieldViewProps } from "../../views/nodes/FieldView";
+
+interface ImageImporterProps {
+    addSchema: (imageDocs: Doc[]) => void;
+}
+
+export default class BulkImporter extends React.Component<FieldViewProps> {
+    private selector = React.createRef<HTMLInputElement>();
+
+    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
+        let promises: Promise<void>[] = [];
+        let docs: Doc[] = [];
+
+        let files = e.target.files;
+        if (!files) return;
+
+        for (let i = 0; i < files.length; i++) {
+            let target = files.item(i);
+
+            if (target === null) {
+                continue;
+            }
+
+            let type = target.type;
+            let formData = new FormData();
+            formData.append('file', target);
+            let dropFileName = target ? target.name : "-empty-";
+
+            let prom = fetch(DocServer.prepend(RouteStore.upload), {
+                method: 'POST',
+                body: formData
+            }).then(async (res: Response) => {
+                (await res.json()).map(action((file: any) => {
+                    let path = window.location.origin + file;
+                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
+                    docPromise.then(doc => doc && docs.push(doc));
+                }));
+            });
+            promises.push(prom);
+        }
+
+        await Promise.all(promises);
+
+        let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" });
+    }
+
+    componentDidMount() {
+        this.selector.current!.setAttribute("directory", "true");
+        this.selector.current!.setAttribute("webkitdirectory", "true");
+    }
+
+    render() {
+        return (
+            <div>
+                <input ref={this.selector} name={"selector"} onChange={this.handleSelection} type="file" style={{ position: "absolute" }} />
+            </div>
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx
new file mode 100644
index 000000000..630911710
--- /dev/null
+++ b/src/client/util/Import & Export/ImportBox.tsx	
@@ -0,0 +1,134 @@
+import "fs";
+import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
+import { DocServer } from "../../DocServer";
+import { RouteStore } from "../../../server/RouteStore";
+import { action, observable } from "mobx";
+import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
+import Measure, { ContentRect } from "react-measure";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faArrowUp } from '@fortawesome/free-solid-svg-icons';
+import { Docs, DocumentOptions } from "../../documents/Documents";
+
+interface ImageImporterProps {
+    addSchema: (imageDocs: Doc[]) => void;
+}
+
+export default class ImportBox extends React.Component<FieldViewProps> {
+    @observable private top = 0;
+    @observable private left = 0;
+    private dimensions = 50;
+
+    constructor(props: FieldViewProps) {
+        super(props);
+        library.add(faArrowUp);
+    }
+
+    public static LayoutString() { return FieldView.LayoutString(ImportBox); }
+
+    private selector = React.createRef<HTMLInputElement>();
+
+    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
+        let promises: Promise<void>[] = [];
+        let docs: Doc[] = [];
+
+        let files = e.target.files;
+        if (!files || files.length === 0) return;
+
+        let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
+
+        for (let i = 0; i < files.length; i++) {
+            let uploaded_file = files.item(i);
+
+            if (!uploaded_file) {
+                continue;
+            }
+
+            let formData = new FormData();
+            formData.append('file', uploaded_file);
+            let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
+            let type = uploaded_file.type;
+
+            let prom = fetch(DocServer.prepend(RouteStore.upload), {
+                method: 'POST',
+                body: formData
+            }).then(async (res: Response) => {
+                (await res.json()).map(action((file: any) => {
+                    let path = DocServer.prepend(file);
+                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
+                    docPromise.then(doc => doc && docs.push(doc));
+                }));
+            });
+            promises.push(prom);
+        }
+
+        await Promise.all(promises);
+
+        let doc = this.props.Document;
+        let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
+        let parent = this.props.ContainingCollectionView;
+        if (parent) {
+            let importContainer = Docs.StackingDocument(docs, options);
+            Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
+            this.props.removeDocument && this.props.removeDocument(doc);
+        }
+    }
+
+    componentDidMount() {
+        this.selector.current!.setAttribute("directory", "true");
+        this.selector.current!.setAttribute("webkitdirectory", "true");
+    }
+
+    @action
+    preserveCentering = (rect: ContentRect) => {
+        let bounds = rect.offset!;
+        if (bounds.width === 0 || bounds.height === 0) {
+            return;
+        }
+        let offset = this.dimensions / 2;
+        this.left = bounds.width / 2 - offset;
+        this.top = bounds.height / 2 - offset;
+    }
+
+    render() {
+        let dimensions = 50;
+        return (
+            <Measure offset onResize={this.preserveCentering}>
+                {({ measureRef }) =>
+                    <div ref={measureRef} style={{ width: "100%", height: "100%", pointerEvents: "all" }} >
+                        <input
+                            id={"selector"}
+                            ref={this.selector}
+                            name={"selector"}
+                            onChange={this.handleSelection}
+                            type="file"
+                            style={{
+                                position: "absolute",
+                                display: "none"
+                            }} />
+                        <label htmlFor={"selector"}>
+                            <div style={{
+                                width: dimensions,
+                                height: dimensions,
+                                borderRadius: "50%",
+                                background: "black",
+                                position: "absolute",
+                                left: this.left,
+                                top: this.top
+                            }} />
+                            <div style={{
+                                position: "absolute",
+                                left: this.left + 12.5,
+                                top: this.top + 11
+                            }}>
+                                <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} />
+                            </div>
+                        </label>
+                    </div>
+                }
+            </Measure>
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 3c396362e..9efef888d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -53,7 +53,7 @@ export namespace SelectionManager {
             stored.length > 0 && (targetColor = stored);
         }
         InkingControl.Instance.updateSelectedColor(targetColor);
-    });
+    }, { fireImmediately: true });
 
     export function DeselectDoc(docView: DocumentView): void {
         manager.DeselectDoc(docView);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ab9fe0118..e38fd3c8b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,6 +38,7 @@ import PDFMenu from './pdf/PDFMenu';
 import { InkTool } from '../../new_fields/InkField';
 import _ from "lodash";
 import KeyManager from './GlobalKeyHandler';
+import BulkImporter from '../util/Import & Export/ImageImporter';
 
 @observer
 export class MainView extends React.Component {
@@ -351,11 +352,13 @@ export class MainView extends React.Component {
         let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
         let addTreeNode = action(() => CurrentUserUtils.UserDocument);
         let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
+        let addImportCollectionNode = action(() => Docs.DirectoryImportDocument({ title: "Directory Import", width: 150, height: 150 }));
 
         let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
             [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode],
             [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
             [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode],
+            [React.createRef<HTMLDivElement>(), "arrow-up", "Import Directory", addImportCollectionNode],
         ];
 
         return < div id="add-nodes-menu" >
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3b3bbdbd9..e886ad84a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -102,47 +102,6 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             return false;
         }
 
-        protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> {
-            let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
-            if (type.indexOf("image") !== -1) {
-                ctor = Docs.ImageDocument;
-            }
-            if (type.indexOf("video") !== -1) {
-                ctor = Docs.VideoDocument;
-            }
-            if (type.indexOf("audio") !== -1) {
-                ctor = Docs.AudioDocument;
-            }
-            if (type.indexOf("pdf") !== -1) {
-                ctor = Docs.PdfDocument;
-                options.nativeWidth = 1200;
-            }
-            if (type.indexOf("excel") !== -1) {
-                ctor = Docs.DBDocument;
-                options.dropAction = "copy";
-            }
-            if (type.indexOf("html") !== -1) {
-                if (path.includes(window.location.hostname)) {
-                    let s = path.split('/');
-                    let id = s[s.length - 1];
-                    DocServer.GetRefField(id).then(field => {
-                        if (field instanceof Doc) {
-                            let alias = Doc.MakeAlias(field);
-                            alias.x = options.x || 0;
-                            alias.y = options.y || 0;
-                            alias.width = options.width || 300;
-                            alias.height = options.height || options.width || 300;
-                            this.props.addDocument(alias, false);
-                        }
-                    });
-                    return undefined;
-                }
-                ctor = Docs.WebDocument;
-                options = { height: options.width, ...options, title: path, nativeWidth: undefined };
-            }
-            return ctor ? ctor(path, options) : undefined;
-        }
-
         @undoBatch
         @action
         protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
@@ -223,7 +182,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         .then(result => {
                             let type = result["content-type"];
                             if (type) {
-                                this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+                                Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }, this.props.addDocument)
                                     .then(doc => doc && this.props.addDocument(doc, false));
                             }
                         });
@@ -245,8 +204,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     }).then(async (res: Response) => {
                         (await res.json()).map(action((file: any) => {
                             let path = window.location.origin + file;
-                            let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
-
+                            let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName }, this.props.addDocument);
                             docPromise.then(doc => doc && this.props.addDocument(doc));
                         }));
                     });
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 0da4888a1..e9fec1588 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,6 +24,7 @@ import { Without, OmitKeys } from "../../../Utils";
 import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { Doc } from "../../../new_fields/Doc";
+import ImportBox from "../../util/Import & Export/ImportBox";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -106,7 +107,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
-            components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, ImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document)}
             jsx={this.finalLayout}
             showWarnings={true}
-- 
cgit v1.2.3-70-g09d2


From aedd283fb9f9eff4145e27658bc6647982256032 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Fri, 5 Jul 2019 00:34:19 -0400
Subject: beginning implementation of key value tags for all imported documents

---
 src/client/documents/Documents.ts                  |  12 +-
 .../util/Import & Export/DirectoryImportBox.scss   |   0
 .../util/Import & Export/DirectoryImportBox.tsx    | 181 +++++++++++++++++++++
 src/client/util/Import & Export/ImageImporter.tsx  |  67 --------
 src/client/util/Import & Export/ImportBox.tsx      | 134 ---------------
 src/client/util/request-image-size.js              |   6 +-
 src/client/views/MainView.tsx                      |   1 -
 src/client/views/collections/CollectionSubView.tsx |   4 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   4 +-
 src/client/views/nodes/ImageBox.tsx                |   4 +-
 10 files changed, 198 insertions(+), 215 deletions(-)
 create mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss
 create mode 100644 src/client/util/Import & Export/DirectoryImportBox.tsx
 delete mode 100644 src/client/util/Import & Export/ImageImporter.tsx
 delete mode 100644 src/client/util/Import & Export/ImportBox.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5d637dd3a..26b5498a2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -36,7 +36,7 @@ import { UndoManager } from "../util/UndoManager";
 import { RouteStore } from "../../server/RouteStore";
 import { LinkManager } from "../util/LinkManager";
 import { DocumentManager } from "../util/DocumentManager";
-import ImportBox from "../util/Import & Export/ImportBox";
+import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
 var requestImageSize = require('../util/request-image-size');
 var path = require('path');
 
@@ -180,7 +180,7 @@ export namespace Docs {
     }
 
     function CreateImportPrototype(): Doc {
-        let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
+        let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", DirectoryImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
         return importProto;
     }
 
@@ -347,7 +347,7 @@ export namespace Docs {
         return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
     }
 
-    export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise<Opt<Doc>> {
+    export async function getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> {
         let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
         if (type.indexOf("image") !== -1) {
             ctor = Docs.ImageDocument;
@@ -370,17 +370,17 @@ export namespace Docs {
             if (path.includes(window.location.hostname)) {
                 let s = path.split('/');
                 let id = s[s.length - 1];
-                DocServer.GetRefField(id).then(field => {
+                return DocServer.GetRefField(id).then(field => {
                     if (field instanceof Doc) {
                         let alias = Doc.MakeAlias(field);
                         alias.x = options.x || 0;
                         alias.y = options.y || 0;
                         alias.width = options.width || 300;
                         alias.height = options.height || options.width || 300;
-                        addDocument && addDocument(alias, false);
+                        return alias;
                     }
+                    return undefined;
                 });
-                return undefined;
             }
             ctor = Docs.WebDocument;
             options = { height: options.width, ...options, title: path, nativeWidth: undefined };
diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
new file mode 100644
index 000000000..2d77f6ae6
--- /dev/null
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -0,0 +1,181 @@
+import "fs";
+import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
+import { DocServer } from "../../DocServer";
+import { RouteStore } from "../../../server/RouteStore";
+import { action, observable, runInAction } from "mobx";
+import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
+import Measure, { ContentRect } from "react-measure";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons';
+import { Docs, DocumentOptions } from "../../documents/Documents";
+import { EditableView } from "../../views/EditableView";
+
+export default class DirectoryImportBox extends React.Component<FieldViewProps> {
+    private selector = React.createRef<HTMLInputElement>();
+    @observable private top = 0;
+    @observable private left = 0;
+    private dimensions = 50;
+
+    @observable private key = "Key";
+    @observable private value = "Value";
+
+    public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); }
+
+    constructor(props: FieldViewProps) {
+        super(props);
+        library.add(faArrowUp, faTag);
+    }
+
+    updateKey = (newKey: string) => {
+        runInAction(() => this.key = newKey);
+        console.log("KEY ", this.key);
+        return true;
+    }
+
+    updateValue = (newValue: string) => {
+        runInAction(() => this.value = newValue);
+        console.log("VALUE ", this.value);
+        return true;
+    }
+
+    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
+        let promises: Promise<void>[] = [];
+        let docs: Doc[] = [];
+
+        let files = e.target.files;
+        if (!files || files.length === 0) return;
+
+        let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
+
+        for (let i = 0; i < files.length; i++) {
+            let uploaded_file = files.item(i);
+
+            if (!uploaded_file) {
+                continue;
+            }
+
+            let formData = new FormData();
+            formData.append('file', uploaded_file);
+            let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
+            let type = uploaded_file.type;
+
+            let prom = fetch(DocServer.prepend(RouteStore.upload), {
+                method: 'POST',
+                body: formData
+            }).then(async (res: Response) => {
+                (await res.json()).map(action((file: any) => {
+                    let path = DocServer.prepend(file);
+                    console.log(path);
+                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
+                    docPromise.then(doc => doc && docs.push(doc));
+                }));
+            });
+            promises.push(prom);
+        }
+
+        await Promise.all(promises);
+
+        let doc = this.props.Document;
+        let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
+        let parent = this.props.ContainingCollectionView;
+        if (parent) {
+            let importContainer = Docs.StackingDocument(docs, options);
+            Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
+            this.props.removeDocument && this.props.removeDocument(doc);
+        }
+    }
+
+    componentDidMount() {
+        this.selector.current!.setAttribute("directory", "");
+        this.selector.current!.setAttribute("webkitdirectory", "");
+    }
+
+    @action
+    preserveCentering = (rect: ContentRect) => {
+        let bounds = rect.offset!;
+        if (bounds.width === 0 || bounds.height === 0) {
+            return;
+        }
+        let offset = this.dimensions / 2;
+        this.left = bounds.width / 2 - offset;
+        this.top = bounds.height / 2 - offset;
+    }
+
+    render() {
+        let dimensions = 50;
+        let keyValueStyle = { paddingLeft: 5, width: "50%" };
+        return (
+            <Measure offset onResize={this.preserveCentering}>
+                {({ measureRef }) =>
+                    <div ref={measureRef} style={{ width: "100%", height: "100%", pointerEvents: "all" }} >
+                        <input
+                            id={"selector"}
+                            ref={this.selector}
+                            name={"selector"}
+                            onChange={this.handleSelection}
+                            type="file"
+                            style={{
+                                position: "absolute",
+                                display: "none"
+                            }} />
+                        <label htmlFor={"selector"}>
+                            <div style={{
+                                width: dimensions,
+                                height: dimensions,
+                                borderRadius: "50%",
+                                background: "black",
+                                position: "absolute",
+                                left: this.left,
+                                top: this.top
+                            }} />
+                            <div style={{
+                                position: "absolute",
+                                left: this.left + 12.5,
+                                top: this.top + 11
+                            }}>
+                                <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} />
+                            </div>
+                        </label>
+                        <div style={{
+                            position: "absolute",
+                            top: 5,
+                            right: 5,
+                            borderRadius: "50%",
+                            width: 25,
+                            height: 25,
+                            background: "black"
+                        }} />
+                        <div style={{
+                            position: "absolute",
+                            right: 9.5,
+                            top: 11
+                        }}>
+                            <FontAwesomeIcon icon={faTag} color="#FFFFFF" size={"1x"} />
+                        </div>
+                        <div style={{ display: "flex", flexDirection: "row", borderBottom: "1px solid black", paddingBottom: 5 }} >
+                            <div className={"key_container"} style={keyValueStyle}>
+                                <EditableView
+                                    contents={this.key}
+                                    SetValue={this.updateKey}
+                                    GetValue={() => this.key}
+                                    oneLine={true}
+                                />
+                            </div>
+                            <div className={"value_container"} style={keyValueStyle}>
+                                <EditableView
+                                    contents={this.value}
+                                    SetValue={this.updateValue}
+                                    GetValue={() => this.value}
+                                    oneLine={true}
+                                />
+                            </div>
+                        </div>
+                    </div>
+                }
+            </Measure>
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx
deleted file mode 100644
index d664f6487..000000000
--- a/src/client/util/Import & Export/ImageImporter.tsx	
+++ /dev/null
@@ -1,67 +0,0 @@
-import "fs";
-import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
-import { DocServer } from "../../DocServer";
-import { RouteStore } from "../../../server/RouteStore";
-import { action } from "mobx";
-import { Docs } from "../../documents/Documents";
-import { FieldViewProps } from "../../views/nodes/FieldView";
-
-interface ImageImporterProps {
-    addSchema: (imageDocs: Doc[]) => void;
-}
-
-export default class BulkImporter extends React.Component<FieldViewProps> {
-    private selector = React.createRef<HTMLInputElement>();
-
-    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
-        let promises: Promise<void>[] = [];
-        let docs: Doc[] = [];
-
-        let files = e.target.files;
-        if (!files) return;
-
-        for (let i = 0; i < files.length; i++) {
-            let target = files.item(i);
-
-            if (target === null) {
-                continue;
-            }
-
-            let type = target.type;
-            let formData = new FormData();
-            formData.append('file', target);
-            let dropFileName = target ? target.name : "-empty-";
-
-            let prom = fetch(DocServer.prepend(RouteStore.upload), {
-                method: 'POST',
-                body: formData
-            }).then(async (res: Response) => {
-                (await res.json()).map(action((file: any) => {
-                    let path = window.location.origin + file;
-                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
-                    docPromise.then(doc => doc && docs.push(doc));
-                }));
-            });
-            promises.push(prom);
-        }
-
-        await Promise.all(promises);
-
-        let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" });
-    }
-
-    componentDidMount() {
-        this.selector.current!.setAttribute("directory", "true");
-        this.selector.current!.setAttribute("webkitdirectory", "true");
-    }
-
-    render() {
-        return (
-            <div>
-                <input ref={this.selector} name={"selector"} onChange={this.handleSelection} type="file" style={{ position: "absolute" }} />
-            </div>
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx
deleted file mode 100644
index 630911710..000000000
--- a/src/client/util/Import & Export/ImportBox.tsx	
+++ /dev/null
@@ -1,134 +0,0 @@
-import "fs";
-import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
-import { DocServer } from "../../DocServer";
-import { RouteStore } from "../../../server/RouteStore";
-import { action, observable } from "mobx";
-import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
-import Measure, { ContentRect } from "react-measure";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faArrowUp } from '@fortawesome/free-solid-svg-icons';
-import { Docs, DocumentOptions } from "../../documents/Documents";
-
-interface ImageImporterProps {
-    addSchema: (imageDocs: Doc[]) => void;
-}
-
-export default class ImportBox extends React.Component<FieldViewProps> {
-    @observable private top = 0;
-    @observable private left = 0;
-    private dimensions = 50;
-
-    constructor(props: FieldViewProps) {
-        super(props);
-        library.add(faArrowUp);
-    }
-
-    public static LayoutString() { return FieldView.LayoutString(ImportBox); }
-
-    private selector = React.createRef<HTMLInputElement>();
-
-    handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
-        let promises: Promise<void>[] = [];
-        let docs: Doc[] = [];
-
-        let files = e.target.files;
-        if (!files || files.length === 0) return;
-
-        let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
-
-        for (let i = 0; i < files.length; i++) {
-            let uploaded_file = files.item(i);
-
-            if (!uploaded_file) {
-                continue;
-            }
-
-            let formData = new FormData();
-            formData.append('file', uploaded_file);
-            let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
-            let type = uploaded_file.type;
-
-            let prom = fetch(DocServer.prepend(RouteStore.upload), {
-                method: 'POST',
-                body: formData
-            }).then(async (res: Response) => {
-                (await res.json()).map(action((file: any) => {
-                    let path = DocServer.prepend(file);
-                    let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
-                    docPromise.then(doc => doc && docs.push(doc));
-                }));
-            });
-            promises.push(prom);
-        }
-
-        await Promise.all(promises);
-
-        let doc = this.props.Document;
-        let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
-        let parent = this.props.ContainingCollectionView;
-        if (parent) {
-            let importContainer = Docs.StackingDocument(docs, options);
-            Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
-            this.props.removeDocument && this.props.removeDocument(doc);
-        }
-    }
-
-    componentDidMount() {
-        this.selector.current!.setAttribute("directory", "true");
-        this.selector.current!.setAttribute("webkitdirectory", "true");
-    }
-
-    @action
-    preserveCentering = (rect: ContentRect) => {
-        let bounds = rect.offset!;
-        if (bounds.width === 0 || bounds.height === 0) {
-            return;
-        }
-        let offset = this.dimensions / 2;
-        this.left = bounds.width / 2 - offset;
-        this.top = bounds.height / 2 - offset;
-    }
-
-    render() {
-        let dimensions = 50;
-        return (
-            <Measure offset onResize={this.preserveCentering}>
-                {({ measureRef }) =>
-                    <div ref={measureRef} style={{ width: "100%", height: "100%", pointerEvents: "all" }} >
-                        <input
-                            id={"selector"}
-                            ref={this.selector}
-                            name={"selector"}
-                            onChange={this.handleSelection}
-                            type="file"
-                            style={{
-                                position: "absolute",
-                                display: "none"
-                            }} />
-                        <label htmlFor={"selector"}>
-                            <div style={{
-                                width: dimensions,
-                                height: dimensions,
-                                borderRadius: "50%",
-                                background: "black",
-                                position: "absolute",
-                                left: this.left,
-                                top: this.top
-                            }} />
-                            <div style={{
-                                position: "absolute",
-                                left: this.left + 12.5,
-                                top: this.top + 11
-                            }}>
-                                <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} />
-                            </div>
-                        </label>
-                    </div>
-                }
-            </Measure>
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js
index 0f9328872..27605d167 100644
--- a/src/client/util/request-image-size.js
+++ b/src/client/util/request-image-size.js
@@ -21,7 +21,9 @@ module.exports = function requestImageSize(options) {
     if (options && typeof options === 'object') {
         opts = Object.assign(options, opts);
     } else if (options && typeof options === 'string') {
-        opts = Object.assign({ uri: options }, opts);
+        opts = Object.assign({
+            uri: options
+        }, opts);
     } else {
         return Promise.reject(new Error('You should provide an URI string or a "request" options object.'));
     }
@@ -70,4 +72,4 @@ module.exports = function requestImageSize(options) {
 
         req.on('error', err => reject(err));
     });
-};
+};
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e38fd3c8b..3d059b2f3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,7 +38,6 @@ import PDFMenu from './pdf/PDFMenu';
 import { InkTool } from '../../new_fields/InkField';
 import _ from "lodash";
 import KeyManager from './GlobalKeyHandler';
-import BulkImporter from '../util/Import & Export/ImageImporter';
 
 @observer
 export class MainView extends React.Component {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e886ad84a..8cb2e1beb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -182,7 +182,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                         .then(result => {
                             let type = result["content-type"];
                             if (type) {
-                                Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }, this.props.addDocument)
+                                Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
                                     .then(doc => doc && this.props.addDocument(doc, false));
                             }
                         });
@@ -204,7 +204,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     }).then(async (res: Response) => {
                         (await res.json()).map(action((file: any) => {
                             let path = window.location.origin + file;
-                            let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName }, this.props.addDocument);
+                            let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
                             docPromise.then(doc => doc && this.props.addDocument(doc));
                         }));
                     });
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e9fec1588..0bfb2954f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,7 +24,7 @@ import { Without, OmitKeys } from "../../../Utils";
 import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { Doc } from "../../../new_fields/Doc";
-import ImportBox from "../../util/Import & Export/ImportBox";
+import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -107,7 +107,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
-            components={{ FormattedTextBox, ImageBox, IconBox, ImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document)}
             jsx={this.finalLayout}
             showWarnings={true}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 06bf65f73..88c122b61 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -213,7 +213,9 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                     }), 0);
                 }
             })
-            .catch((err: any) => console.log(err));
+            .catch((err: any) => {
+                console.log(err);
+            });
     }
 
     render() {
-- 
cgit v1.2.3-70-g09d2


From 336266551d363df8c4958d485170846b478f1787 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 5 Jul 2019 11:54:39 -0400
Subject: fixed templates (old style) a bit.

---
 deploy/assets/downarrow.png                              | Bin 0 -> 3363 bytes
 src/client/views/Templates.tsx                           |   3 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx        |   4 ++--
 src/client/views/nodes/DocumentContentsView.tsx          |   5 ++++-
 4 files changed, 7 insertions(+), 5 deletions(-)
 create mode 100644 deploy/assets/downarrow.png

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

diff --git a/deploy/assets/downarrow.png b/deploy/assets/downarrow.png
new file mode 100644
index 000000000..3c59ff5b1
Binary files /dev/null and b/deploy/assets/downarrow.png differ
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 4843a70a6..236704fa2 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -69,8 +69,7 @@ export namespace Templates {
         `< div >
         <div style="height:100%; width:100%;position:absolute;">{layout}</div>
         <div id="isExpander" style="height:15px; width:15px; margin-left:-16px; pointer-events:all; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white;">
-            <img id="isExpander" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAZlBMVEX///8AAABmZmb7+/tYWFhgYGBFRUVSUlL4+Pg/Pz9jY2N5eXmcnJyioqKBgYFzc3NtbW1LS0s3NzfW1taWlpaOjo6IiIgvLy9WVlampqZcXFw5OTlvb28mJiYxMTHe3t7l5eUjIyMY8kIZAAAD2UlEQVR4nO2d61biMBRGW1FBEVHxfp15/5ecOVa5lHxtArmck/Xtn1BotjtNoXQtm4YQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEFIrX6UHEA1gsmrneceRjHm7cj28attKFOf/TRyKIliH4vzbZE+xE2zbZYkxRWX5Y9JT/BW0X3G+NtlR3Ahar7jcMtlS3Ba0XXG+Y7JW3BW0XHHZM/lR7AvaVewL/ijuC1pV3Bf8VnQJ2lR0CYriq/Nxg4puwfa1aZ7dz9yUHnEgN26NZ3luWkPFd7fEtHsWVDwpO+YgTgYKCuYn6tAU7TBecaygcGpZEQie7m5luKJPQQFUvCwx5iAuvQoK4KShvSIoOHVtCz7dnOUecxBn7kG/urc2eCz6T9EOcxXDCgpAUetyAwoOCBqrGF5QMKR4mCA8L+pTBIJwkRl95eifJjPHTDYTFQ8vePyrs3BsBfXLzfFHkvKKMY4j1ctNnCmmuGKslfCQT0RZiPdFVmnFmOcy36sDWYn7DU9hxdifRkKuEGQh/pWW0K/QiUlxtUxVxTTXyhQtN6kuI6mpmO5qpxJFIBjl1yMVimmvV4PfrnIq3iYsKICTRj7F9L84gIq38fYwCCj4HnMfRY/FPL8ZFayYo6BQbLlJeZrYpVDFXAUFcMtKWkUgmOhmnwKKOQsK4NaxdIp5CwqZj8X8gv27jNecJ9nZuXtnie/SzjhRQcHkt6Fnq1imoAAUY1csVVDIUrFcQSGDIhC8jriLQZIrli0oXKdVLF1QSFqxfEEBVLyI8NYXCgoKySaqhinakajimxrBRBX1FBQSVNRyDP4SXVGbYHRFfYJN8xhTESwyj5HHHEjEihoLCqDiXfAb3aksKESqCAoqEIxUUW9BAS03E+93mOhcZDYcXVF3QeHBPcI3v4qo4EPiUQcBKr75vHaiv6AAKt6NV0SCqgoKqOKYovpFZgOo+DmsOHkyUlA4ZKKamaIdQPEJK5oqKKCKM7D9zFZBIayiuYICWm5cFWef7o3vs486CP8VdQIEVRcU7sFE7VecgSmqvKDgVxEJqi8ogIof2xVnH2YLCuMT1fAU7RirOPtrXHCsovmCwlDFCgoKWNH4IrMBTdQ/NUzRjiu3CeCq9HAPAVSspaDgX9FkQcG3ollB34qGBf0UTQv6KBoXHFc0LzimWIFg0ywGBBelBxcHXLGKggKqWElBwV2xIkF3xaoEXYqVCe4rVifYV3wpPZwULOouKLzUXVBY1F1QeKm7oLCoXVAqVi7YNM7/F0YIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCG+/ANh4i1CHdc63QAAAABJRU5ErkJggg=="
-                width="15px" height="15px" />
+            <img id="isExpander" src="/assets/downarrow.png" width="15px" height="15px" />
         </div>
         </div > `
     );
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4b7d462e5..7c0591600 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -468,8 +468,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
             description: "Add freeform arrangement",
             event: () => {
                 let addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record<string, string>, requiredType?: string) => {
-                    const docs = DocListCast(this.Document[this.props.fieldKey]);
-                    docs.map(d => d.transition = "transform 1s");
                     let overlayDisposer: () => void;
                     const script = this.Document[key];
                     let originalText: string | undefined = undefined;
@@ -484,6 +482,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                             onError(script.errors.map(error => error.messageText).join("\n"));
                             return;
                         }
+                        const docs = DocListCast(this.Document[this.props.fieldKey]);
+                        docs.map(d => d.transition = "transform 1s");
                         this.Document[key] = new ScriptField(script);
                         overlayDisposer();
                         setTimeout(() => docs.map(d => d.transition = undefined), 1200);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 0da4888a1..51d0c83cd 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,6 +24,7 @@ import { Without, OmitKeys } from "../../../Utils";
 import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { Doc } from "../../../new_fields/Doc";
+import { CollectionViewType } from "../collections/CollectionBaseView";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -82,7 +83,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         // by checking the layoutKey.  This should probably be moved into
         // a prop so that the overlay can explicitly turn off templates.
         if ((this.props.layoutKey === "overlayLayout" && StrCast(this.props.Document.layout).indexOf("CollectionView") !== -1) ||
-            (this.props.layoutKey === "layout" && StrCast(this.props.Document.layout).indexOf("CollectionView") === -1)) {
+            (this.props.layoutKey === "layout" && StrCast(this.props.Document.layout).indexOf("CollectionView") === -1) ||
+            (this.props.layoutKey === "layout" && NumCast(this.props.Document.viewType)) !== CollectionViewType.Freeform) {
             this.templates.forEach(template => {
                 let self = this;
                 // this scales constants in the markup by the scaling applied to the document, but caps the constants to be smaller
@@ -103,6 +105,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     render() {
+        console.log(this.props.Document.title);
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
-- 
cgit v1.2.3-70-g09d2


From d27eea0db5e5fc3de0b5ee0e58fc2db3377c4131 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Fri, 5 Jul 2019 14:53:38 -0400
Subject: Added some more search util functions

---
 src/client/util/SearchUtil.ts                   | 33 +++++++++++++++++++++----
 src/client/views/nodes/DocumentContentsView.tsx |  1 -
 2 files changed, 28 insertions(+), 6 deletions(-)

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

diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index ac2eb72e7..338628960 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -29,11 +29,16 @@ export namespace SearchUtil {
         return { docs, numFound };
     }
 
-    export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]> {
-        const proto = await Doc.GetT(doc, "proto", Doc, true);
-        const protoId = (proto || doc)[Id];
-        const result = await Search(`proto_i:"${protoId}"`, true);
-        return result.docs;
+    export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
+    export async function GetAliasesOfDocument(doc: Doc, returnDocs: false): Promise<string[]>;
+    export async function GetAliasesOfDocument(doc: Doc, returnDocs = true): Promise<Doc[] | string[]> {
+        const proto = Doc.GetProto(doc);
+        const protoId = proto[Id];
+        if (returnDocs) {
+            return (await Search(`proto_i:"${protoId}"`, returnDocs)).docs;
+        } else {
+            return (await Search(`proto_i:"${protoId}"`, returnDocs)).ids;
+        }
         // return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
     }
 
@@ -41,4 +46,22 @@ export namespace SearchUtil {
         const results = await Search(`proto_i:"${doc[Id]}"`, true);
         return results.docs;
     }
+
+    export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[], aliasContexts: Doc[] }> {
+        const docContexts = (await Search(`data_l:"${doc[Id]}"`, true)).docs;
+        const aliases = await GetAliasesOfDocument(doc, false);
+        const aliasContexts = (await Promise.all(aliases.map(doc => Search(`data_l:"${doc}"`, true))));
+        const contexts = { contexts: docContexts, aliasContexts: [] as Doc[] };
+        aliasContexts.forEach(result => contexts.aliasContexts.push(...result.docs));
+        return contexts;
+    }
+
+    export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[], aliasContexts: string[] }> {
+        const docContexts = (await Search(`data_l:"${doc[Id]}"`, false)).ids;
+        const aliases = await GetAliasesOfDocument(doc, false);
+        const aliasContexts = (await Promise.all(aliases.map(doc => Search(`data_l:"${doc}"`, false))));
+        const contexts = { contexts: docContexts, aliasContexts: [] as string[] };
+        aliasContexts.forEach(result => contexts.aliasContexts.push(...result.ids));
+        return contexts;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 51d0c83cd..56a14e26e 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -105,7 +105,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     render() {
-        console.log(this.props.Document.title);
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
-- 
cgit v1.2.3-70-g09d2


From bc4e7ceb8a738b890344c4564ab0d97e0a10293d Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 9 Jul 2019 10:57:58 -0400
Subject: fixed some template issues, added text templates.

---
 src/client/views/DocumentDecorations.tsx           |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  3 +-
 src/client/views/nodes/DocumentContentsView.tsx    | 27 +++++++++---
 src/client/views/nodes/DocumentView.tsx            | 15 ++++++-
 src/client/views/nodes/FormattedTextBox.tsx        | 48 +++++++++++++---------
 5 files changed, 65 insertions(+), 30 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 48b89ff4e..d6cf793ab 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -357,7 +357,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
     onRadiusMove = (e: PointerEvent): void => {
         let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1]));
-        SelectionManager.SelectedDocuments().map(dv => Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`);
+        SelectionManager.SelectedDocuments().map(dv => dv.props.Document.borderRounding = Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`);
         e.stopPropagation();
         e.preventDefault();
     }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 1c00687ed..b09538d1a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -7,6 +7,7 @@ import { DocComponent } from "../DocComponent";
 import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
 import "./DocumentView.scss";
 import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     x?: number;
@@ -70,7 +71,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
 
     borderRounding = () => {
-        let br = StrCast(this.props.Document.borderRounding);
+        let br = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.borderRounding : this.props.Document.borderRounding);
         if (br.endsWith("%")) {
             let percent = Number(br.substr(0, br.length - 1)) / 100;
             let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight));
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 56a14e26e..745520bc4 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -49,12 +49,11 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     hideOnLeave?: boolean
 }> {
     @computed get layout(): string {
-        let layoutDoc = this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
-        const layout = Cast(layoutDoc[this.props.layoutKey], "string");
+        const layout = Cast(this.layoutDoc[this.props.layoutKey], "string");
         if (layout === undefined) {
             return this.props.Document.data ?
                 "<FieldView {...props} fieldKey='data' />" :
-                KeyValueBox.LayoutString(layoutDoc.proto ? "proto" : "");
+                KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
         } else if (typeof layout === "string") {
             return layout;
         } else {
@@ -62,8 +61,23 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         }
     }
 
-    CreateBindings(layoutDoc?: Doc): JsxBindings {
-        return { props: { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: layoutDoc } };
+    get dataDoc() {
+        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+            // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
+            // has a template layout document, then we will render the template layout but use 
+            // this document as the data document for the layout.
+            return this.props.Document;
+        }
+        return this.props.DataDoc
+    }
+    get layoutDoc() {
+        // if this document's layout field contains a document (ie, a rendering template), then we will use that
+        // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string.
+        return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
+    }
+
+    CreateBindings(): JsxBindings {
+        return { props: { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: this.layoutDoc, DataDoc: this.dataDoc } };
     }
 
     @computed get templates(): List<string> {
@@ -105,11 +119,12 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     render() {
+        let self = this;
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
             components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
-            bindings={this.CreateBindings(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document)}
+            bindings={this.CreateBindings()}
             jsx={this.finalLayout}
             showWarnings={true}
             onError={(test: any) => { console.log(test); }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d027ed516..47ec3b5a1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -218,7 +218,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         e.stopPropagation();
     }
 
-    get dataDoc() { return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; }
+    get dataDoc() {
+        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+            // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
+            // has a template layout document, then we will render the template layout but use 
+            // this document as the data document for the layout.
+            return this.props.Document;
+        }
+        return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
+    }
     startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
         if (this._mainCont.current) {
             let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
@@ -585,20 +593,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @computed get nativeWidth() { return this.Document.nativeWidth || 0; }
     @computed get nativeHeight() { return this.Document.nativeHeight || 0; }
     @computed get contents() {
-        return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.props.selectOnLoad} layoutKey={"layout"} />);
+        return (<DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.props.selectOnLoad} layoutKey={"layout"} DataDoc={this.dataDoc} />);
     }
 
     render() {
         if (this.Document.hidden) {
             return null;
         }
+        let self = this;
         let backgroundColor = this.props.Document.layout instanceof Doc ? StrCast(this.props.Document.layout.backgroundColor) : this.Document.backgroundColor;
+        let foregroundColor = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.color : this.props.Document.color);
         var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
         var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
                 style={{
+                    color: foregroundColor,
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
                     outlineWidth: BoolCast(this.props.Document.libraryBrush) && !StrCast(this.props.Document.borderRounding) ?
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index bf6f4c764..68a4372d5 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -99,7 +99,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     @undoBatch
     public setFontColor(color: string) {
+        let self = this;
         if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false;
+        if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) {
+            this.props.Document.color = color;
+        }
         let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color });
         this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from,
             this._editorView!.state.selection.to, colorMark));
@@ -160,25 +164,28 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             e.stopPropagation();
         } else {
             if (de.data instanceof DragManager.DocumentDragData) {
-                let ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
-                if (!ldocs) {
-                    this.dataDoc.subBulletDocs = new List<Doc>([]);
-                }
-                ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
-                if (!ldocs) return;
-                if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
-                    ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.dataDoc.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
-                    this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
-                    this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
-                    this.props.Document.isBullet = true;
-                }
-                let stackDoc = (ldocs[0] as Doc);
-                if (de.data.moveDocument) {
-                    de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
-                        Cast(stackDoc.data, listSpec(Doc))!.push(doc);
-                        return true;
-                    });
-                }
+                this.props.Document.layout = de.data.draggedDocuments[0];
+                de.data.draggedDocuments[0].isTemplate = true;
+                e.stopPropagation();
+                // let ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
+                // if (!ldocs) {
+                //     this.dataDoc.subBulletDocs = new List<Doc>([]);
+                // }
+                // ldocs = Cast(this.dataDoc.subBulletDocs, listSpec(Doc));
+                // if (!ldocs) return;
+                // if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
+                //     ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.dataDoc.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
+                //     this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
+                //     this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
+                //     this.props.Document.isBullet = true;
+                // }
+                // let stackDoc = (ldocs[0] as Doc);
+                // if (de.data.moveDocument) {
+                //     de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
+                //         Cast(stackDoc.data, listSpec(Doc))!.push(doc);
+                //         return true;
+                //     });
+                // }
             }
         }
     }
@@ -426,6 +433,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems });
     }
     render() {
+        let self = this;
         let style = this.props.isOverlay ? "scroll" : "hidden";
         let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
         let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
@@ -435,7 +443,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                     height: this.props.height ? this.props.height : undefined,
                     background: this.props.hideOnLeave ? "rgba(0,0,0,0.4)" : undefined,
                     opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || this.props.Document.libraryBrush ? 1 : 0.1) : 1,
-                    color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "initial",
+                    color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit",
                     pointerEvents: interactive ? "all" : "none",
                     fontSize: "13px"
                 }}
-- 
cgit v1.2.3-70-g09d2


From 7fdb33cafaa3e8593d648ddba994356a9625ff56 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Tue, 9 Jul 2019 13:52:35 -0400
Subject: string list to list of text doc map in scraper and cleaned up kv pane
 templating interaction code

---
 src/client/views/nodes/DocumentContentsView.tsx |  7 +--
 src/client/views/nodes/KeyValueBox.tsx          | 20 ++++++--
 src/scraping/buxton/scraper.py                  | 61 +++++++++++++++++++++++--
 3 files changed, 75 insertions(+), 13 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 b4c0e844f..eb786d537 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -69,7 +69,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
             // this document as the data document for the layout.
             return this.props.Document;
         }
-        return this.props.DataDoc
+        return this.props.DataDoc;
     }
     get layoutDoc() {
         // if this document's layout field contains a document (ie, a rendering template), then we will use that
@@ -124,13 +124,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         if (this.props.renderDepth > 7) return (null);
         if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
         return <ObserverJsxParser
-<<<<<<< HEAD
             components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
-            bindings={this.CreateBindings(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document)}
-=======
-            components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings()}
->>>>>>> b49fdb1c42b9758e006521e0f404634ba396a2ac
             jsx={this.finalLayout}
             showWarnings={true}
             onError={(test: any) => { console.log(test); }}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index e27ab1589..e8619584e 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -161,7 +161,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     getTemplate = async () => {
         let parent = Docs.StackingDocument([], { width: 800, height: 800, title: "Template" });
         parent.singleColumn = false;
-        parent.columnWidth = 50;
+        parent.columnWidth = 100;
         for (let row of this.rows.filter(row => row.isChecked)) {
             await this.createTemplateField(parent, row);
             row.uncheck();
@@ -175,7 +175,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         if (!sourceDoc) {
             return;
         }
-        let fieldTemplate = this.inferType(sourceDoc[metaKey], metaKey);
+        let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
 
         // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
         let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
@@ -200,12 +200,24 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate);
     }
 
-    inferType = (data: FieldResult, metaKey: string) => {
+    inferType = async (data: FieldResult, metaKey: string) => {
         let options = { width: 300, height: 300, title: metaKey };
         if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") {
             return Docs.TextDocument(options);
         } else if (data instanceof List) {
-            return Docs.StackingDocument([], options);
+            if (data.length === 0) {
+                return Docs.StackingDocument([], options);
+            }
+            let first = await Cast(data[0], Doc);
+            if (!first) {
+                return Docs.StackingDocument([], options);
+            }
+            switch (first.type) {
+                case "image":
+                    return Docs.StackingDocument([], options);
+                case "text":
+                    return Docs.TreeDocument([], options);
+            }
         } else if (data instanceof ImageField) {
             return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options);
         }
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index 02c6d8b74..700269727 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -26,7 +26,7 @@ def extract_links(fileName):
         item = rels[rel]
         if item.reltype == RT.HYPERLINK and ".aspx" not in item._target:
             links.append(item._target)
-    return listify(links)
+    return text_doc_map(links)
 
 
 def extract_value(kv_string):
@@ -60,6 +60,12 @@ def protofy(fieldId):
     }
 
 
+def text_doc_map(string_list):
+    def guid_map(caption):
+        return write_text_doc(caption)
+    return listify(proxify_guids(list(map(guid_map, string_list))))
+
+
 def write_schema(parse_results, display_fields, storage_key):
     view_guids = parse_results["child_guids"]
 
@@ -110,6 +116,54 @@ def write_schema(parse_results, display_fields, storage_key):
     return view_doc_guid
 
 
+def write_text_doc(content):
+    data_doc_guid = guid()
+    view_doc_guid = guid()
+
+    view_doc = {
+        "_id": view_doc_guid,
+        "fields": {
+            "proto": protofy(data_doc_guid),
+            "x": 10,
+            "y": 10,
+            "width": 400,
+            "zIndex": 2,
+            "libraryBrush": False
+        },
+        "__type": "Doc"
+    }
+
+    data_doc = {
+        "_id": data_doc_guid,
+        "fields": {
+            "proto": protofy("textProto"),
+            "data": {
+                "Data": '{"doc":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"' + content + '"}]}]},"selection":{"type":"text","anchor":1,"head":1}' + '}',
+                "__type": "RichTextField"
+            },
+            "title": content,
+            "nativeWidth": 200,
+            "author": "Bill Buxton",
+            "creationDate": {
+                "date": datetime.datetime.utcnow().microsecond,
+                "__type": "date"
+            },
+            "isPrototype": True,
+            "autoHeight": True,
+            "page": -1,
+            "nativeHeight": 200,
+            "height": 200,
+            "data_text": content
+        },
+        "__type": "Doc"
+    }
+
+    db.newDocuments.insert_one(view_doc)
+    db.newDocuments.insert_one(data_doc)
+
+    return view_doc_guid
+
+
 def write_image(folder, name):
     path = f"http://localhost:1050/files/{folder}/{name}"
 
@@ -253,7 +307,7 @@ def parse_document(file_name: str):
     while lines[cur] != "Image":
         link_descriptions.append(lines[cur].strip())
         cur += 1
-    result["link_descriptions"] = listify(link_descriptions)
+    result["link_descriptions"] = text_doc_map(link_descriptions)
 
     result["hyperlinks"] = extract_links(source + "/" + file_name)
 
@@ -265,7 +319,8 @@ def parse_document(file_name: str):
         captions.append(lines[cur + 1])
         cur += 2
     result["images"] = listify(images)
-    result["captions"] = listify(captions)
+
+    result["captions"] = text_doc_map(captions)
 
     notes = []
     if (cur < len(lines) and lines[cur] == "NOTES:"):
-- 
cgit v1.2.3-70-g09d2


From bcbfc7435a3b2234d8ac5244c4209d82f1732da0 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 10 Jul 2019 12:54:55 -0400
Subject: switched title/caption templates to be part of DocumentViews

---
 .../views/collections/CollectionSchemaView.tsx     |  2 ++
 .../views/collections/CollectionStackingView.tsx   |  7 +++++
 src/client/views/nodes/DocumentContentsView.tsx    | 29 +------------------
 src/client/views/nodes/DocumentView.tsx            | 33 ++++++++++++++++++++--
 4 files changed, 41 insertions(+), 30 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index b54e8aff0..148711c97 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -405,6 +405,7 @@ interface CollectionSchemaPreviewProps {
     renderDepth: number;
     width: () => number;
     height: () => number;
+    showOverlays?: (doc: Doc) => { title?: boolean, caption?: boolean };
     CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView;
     getTransform: () => Transform;
     addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -488,6 +489,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
                         fitToBox={this.props.fitToBox}
                         renderDepth={this.props.renderDepth + 1}
                         selectOnLoad={false}
+                        showOverlays={this.props.showOverlays}
                         addDocument={this.props.addDocument}
                         removeDocument={this.props.removeDocument}
                         moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index aea74321e..e6a3e8b68 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -12,6 +12,7 @@ import "./CollectionStackingView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import { undoBatch } from "../../util/UndoManager";
 import { DragManager } from "../../util/DragManager";
+import { DocTypes } from "../../documents/Documents";
 
 @observer
 export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -61,6 +62,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
         this.createDropTarget(ele!);
     }
 
+    overlays = (doc: Doc) => {
+        return doc.type === DocTypes.IMG ? { title: true, caption: true } : {}
+    }
+
     @computed
     get singleColumnChildren() {
         return this.filteredChildren.map((d, i) => {
@@ -75,6 +80,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                 <CollectionSchemaPreview
                     Document={layoutDoc}
                     DataDocument={d !== this.props.DataDoc ? this.props.DataDoc : undefined}
+                    showOverlays={this.overlays}
                     renderDepth={this.props.renderDepth}
                     width={width}
                     height={height}
@@ -122,6 +128,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
                     addDocument={this.props.addDocument}
                     moveDocument={this.props.moveDocument}
                     removeDocument={this.props.removeDocument}
+                    showOverlays={this.overlays}
                     getTransform={dxf}
                     width={width}
                     height={height}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index eb786d537..ed6b224a7 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -89,34 +89,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         return new List<string>();
     }
     @computed get finalLayout() {
-        const baseLayout = this.props.layoutKey === "overlayLayout" ? "<div/>" : this.layout;
-        let base = baseLayout;
-        let layout = baseLayout;
-
-        // bcz: templates are intended only for a document's primary layout or overlay (not background).  However, 
-        // a DocumentContentsView is used to render  annotation overlays, so we detect that here 
-        // by checking the layoutKey.  This should probably be moved into
-        // a prop so that the overlay can explicitly turn off templates.
-        if ((this.props.layoutKey === "overlayLayout" && StrCast(this.props.Document.layout).indexOf("CollectionView") !== -1) ||
-            (this.props.layoutKey === "layout" && StrCast(this.props.Document.layout).indexOf("CollectionView") === -1) ||
-            (this.props.layoutKey === "layout" && NumCast(this.props.Document.viewType)) !== CollectionViewType.Freeform) {
-            this.templates.forEach(template => {
-                let self = this;
-                // this scales constants in the markup by the scaling applied to the document, but caps the constants to be smaller
-                // than the width/height of the containing document
-                function convertConstantsToNative(match: string, offset: number, x: string) {
-                    let px = Number(match.replace("px", ""));
-                    return `${Math.min(NumCast(self.props.Document.height, 0),
-                        Math.min(NumCast(self.props.Document.width, 0),
-                            px * self.props.ScreenToLocalTransform().Scale))}px`;
-                }
-                // let nativizedTemplate = template.replace(/([0-9]+)px/g, convertConstantsToNative);
-                // layout = nativizedTemplate.replace("{layout}", base);
-                layout = template.replace("{layout}", base);
-                base = layout;
-            });
-        }
-        return layout;
+        return this.props.layoutKey === "overlayLayout" ? "<div/>" : this.layout;
     }
 
     render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 430409ee3..5385238f0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -8,7 +8,7 @@ import { ObjectField } from "../../../new_fields/ObjectField";
 import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
 import { BoolCast, Cast, FieldValue, StrCast, NumCast, PromiseValue } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { emptyFunction, Utils } from "../../../Utils";
+import { emptyFunction, Utils, returnFalse, returnTrue } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
@@ -34,6 +34,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
 import { list, object, createSimpleSchema } from 'serializr';
 import { LinkManager } from '../../util/LinkManager';
 import { RouteStore } from '../../../server/RouteStore';
+import { FormattedTextBox } from './FormattedTextBox';
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 library.add(fa.faTrash);
@@ -78,6 +79,7 @@ export interface DocumentViewProps {
     moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
     ScreenToLocalTransform: () => Transform;
     renderDepth: number;
+    showOverlays?: (doc: Doc) => { title?: boolean, caption?: boolean };
     ContentScaling: () => number;
     PanelWidth: () => number;
     PanelHeight: () => number;
@@ -593,6 +595,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let foregroundColor = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.color : this.props.Document.color);
         var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
         var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
+        let showTitle = this.props.showOverlays && this.props.showOverlays(this.props.Document).title;
+        let showCaption = this.props.showOverlays && this.props.showOverlays(this.props.Document).caption;
+        let templates = Cast(this.props.Document.templates, listSpec("string"));
+        if (templates instanceof List) {
+            templates.map(str => {
+                if (str.indexOf("{props.Document.title}") !== -1) showTitle = true;
+                if (str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = true;
+            });
+        }
         return (
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 ref={this._mainCont}
@@ -614,7 +625,25 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
                 onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
             >
-                {this.contents}
+                {!showTitle && !showCaption ? this.contents :
+                    <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}>
+                        {!showTitle ? (null) :
+                            <div style={{
+                                position: "absolute", top: 0, textAlign: "center", textOverflow: "ellipsis", whiteSpace: "pre",
+                                overflow: "hidden", width: `${100 * this.props.ContentScaling()}%`, height: 25, background: "rgba(0, 0, 0, .4)", color: "white",
+                                transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})`
+                            }}>
+                                <span>{this.props.Document.title}</span>
+                            </div>
+                        }
+                        {!showCaption ? (null) :
+                            <div style={{ position: "absolute", bottom: 0, transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+                                <FormattedTextBox {...this.props} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} selectOnLoad={this.props.selectOnLoad} fieldExt={""} hideOnLeave={true} fieldKey={"caption"} />
+                            </div>
+                        }
+                        {this.contents}
+                    </div>
+                }
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2

