From de6e2427cf5eed2805432a37b5284a4c8b934e62 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 17 Jul 2019 12:49:12 -0400
Subject: fixed some search box things. added more to audio annotations.

---
 src/client/views/nodes/ImageBox.tsx | 67 ++++++++++++++++++++++++++++++++++---
 1 file changed, 62 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4c5ad7a7d..7c29724a2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,6 +1,6 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faImage } from '@fortawesome/free-solid-svg-icons';
-import { action, observable, computed } from 'mobx';
+import { faImage, faFileAudio } from '@fortawesome/free-solid-svg-icons';
+import { action, observable, computed, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import Lightbox from 'react-image-lightbox';
 import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
@@ -8,7 +8,7 @@ import { Doc, HeightSym, WidthSym, DocListCast } from '../../../new_fields/Doc';
 import { List } from '../../../new_fields/List';
 import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
 import { Cast, FieldValue, NumCast, StrCast, BoolCast } from '../../../new_fields/Types';
-import { ImageField } from '../../../new_fields/URLField';
+import { ImageField, AudioField } from '../../../new_fields/URLField';
 import { Utils } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
@@ -23,11 +23,15 @@ import React = require("react");
 import { RouteStore } from '../../../server/RouteStore';
 import { Docs } from '../../documents/Documents';
 import { DocServer } from '../../DocServer';
+import { Font } from '@react-pdf/renderer';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 var requestImageSize = require('../../util/request-image-size');
 var path = require('path');
+const { Howl, Howler } = require('howler');
 
 
 library.add(faImage);
+library.add(faFileAudio);
 
 
 export const pageSchema = createSchema({
@@ -177,10 +181,11 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                     audioAnnos.push(audioDoc);
                 }
             };
+            runInAction(() => self._audioState = 2);
             recorder.start();
             setTimeout(() => {
                 recorder.stop();
-
+                runInAction(() => self._audioState = 0);
                 gumStream.getAudioTracks()[0].stop();
             }, 5000);
         });
@@ -272,6 +277,46 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
             });
     }
 
+    @observable _audioState = 0;
+
+    @action
+    onPointerEnter = () => {
+        let self = this;
+        let audioAnnos = DocListCast(this.extensionDoc.audioAnnotations);
+        if (audioAnnos.length && this._audioState === 0) {
+            audioAnnos.map(anno => anno.data instanceof AudioField && new Howl({
+                src: [anno.data.url.href],
+                autoplay: true,
+                loop: false,
+                volume: 0.5,
+                onend: function () {
+                    runInAction(() => self._audioState = 0);
+                }
+            }));
+            this._audioState = 1;
+        }
+        else {
+            if (this._audioState === 0) {
+                this._audioState = 1;
+                new Howl({
+                    src: ["https://www.kozco.com/tech/piano2-CoolEdit.mp3"],
+                    autoplay: true,
+                    loop: false,
+                    volume: 0.5,
+                    onend: function () {
+                        runInAction(() => self._audioState = 0);
+                    }
+                });
+            }
+        }
+    }
+
+    @action
+    audioDown = () => {
+        this.recordAudioAnnotation();
+    }
+
+
     render() {
         // let transform = this.props.ScreenToLocalTransform().inverse();
         let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
@@ -282,7 +327,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx
         let nativeWidth = FieldValue(this.Document.nativeWidth, pw);
         let nativeHeight = FieldValue(this.Document.nativeHeight, 0);
-        let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"];
+        let paths: string[] = [window.origin + RouteStore.corsProxy + "/" + "http://www.cs.brown.edu/~bcz/noImage.png"];
         // this._curSuffix = "";
         // if (w > 20) {
         Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
@@ -305,6 +350,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
 
         return (
             <div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
+                onPointerEnter={this.onPointerEnter}
                 onPointerDown={this.onPointerDown}
                 onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <img id={id}
@@ -316,6 +362,17 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                     ref={this._imgRef}
                     onError={this.onError} />
                 {paths.length > 1 ? this.dots(paths) : (null)}
+                <div onPointerDown={this.audioDown} style={{
+                    display: DocListCast(this.extensionDoc.audioAnnotations).length ? "inline" : "inline",
+                    width: nativeWidth * 0.1,
+                    height: nativeWidth * 0.1,
+                    position: "absolute",
+                    top: 0,
+                    left: 0,
+                }}>
+                    <FontAwesomeIcon
+                        style={{ width: "100%", height: "100%", color: [DocListCast(this.extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={faFileAudio} size="sm" />
+                </div>
                 {/* {this.lightbox(paths)} */}
             </div>);
     }
-- 
cgit v1.2.3-70-g09d2


From 2f9aadfce9a1d8c26457bc56b1b095ae625be77b Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 17 Jul 2019 12:51:22 -0400
Subject: remove testing code.

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

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

diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 7c29724a2..a3e098fd8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -295,20 +295,20 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
             }));
             this._audioState = 1;
         }
-        else {
-            if (this._audioState === 0) {
-                this._audioState = 1;
-                new Howl({
-                    src: ["https://www.kozco.com/tech/piano2-CoolEdit.mp3"],
-                    autoplay: true,
-                    loop: false,
-                    volume: 0.5,
-                    onend: function () {
-                        runInAction(() => self._audioState = 0);
-                    }
-                });
-            }
-        }
+        // else {
+        //     if (this._audioState === 0) {
+        //         this._audioState = 1;
+        //         new Howl({
+        //             src: ["https://www.kozco.com/tech/piano2-CoolEdit.mp3"],
+        //             autoplay: true,
+        //             loop: false,
+        //             volume: 0.5,
+        //             onend: function () {
+        //                 runInAction(() => self._audioState = 0);
+        //             }
+        //         });
+        //     }
+        // }
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From 516c0b35bbb7a184931ab0fe1d54086d8052bda4 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 17 Jul 2019 15:15:49 -0400
Subject: changed image proxying stuff

---
 src/client/documents/Documents.ts   | 5 +++--
 src/client/views/nodes/ImageBox.tsx | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index af2b95659..3c248760b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -311,8 +311,9 @@ export namespace Docs {
         }
 
         export function ImageDocument(url: string, options: DocumentOptions = {}) {
-            let inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), new ImageField(new URL(url)), { title: path.basename(url), ...options });
-            requestImageSize(window.origin + RouteStore.corsProxy + "/" + url)
+            let imgField = new ImageField(new URL(url));
+            let inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
+            requestImageSize(imgField.url.href)
                 .then((size: any) => {
                     let aspect = size.height / size.width;
                     if (!inst.proto!.nativeWidth) {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index a3e098fd8..ebe3732e6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -259,7 +259,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     _curSuffix = "_m";
 
     resize(srcpath: string, layoutdoc: Doc) {
-        requestImageSize(window.origin + RouteStore.corsProxy + "/" + srcpath)
+        requestImageSize(srcpath)
             .then((size: any) => {
                 let aspect = size.height / size.width;
                 let rotation = NumCast(this.dataDoc.rotation) % 180;
-- 
cgit v1.2.3-70-g09d2


From 5aa6bcdd4e23bb9e9d05181d0dc6b638e45e397b Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 17 Jul 2019 20:48:14 -0400
Subject: Moved DocServer.prepend and changed how corsProxy works

---
 src/Utils.ts                                       | 13 +++++
 src/client/DocServer.ts                            |  8 ----
 src/client/util/History.ts                         |  2 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |  4 +-
 src/client/util/SearchUtil.ts                      |  3 +-
 src/client/util/TooltipTextMenu.tsx                |  8 ++--
 src/client/views/MainView.tsx                      |  4 +-
 src/client/views/SearchBox.tsx                     |  3 +-
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  7 +--
 src/client/views/nodes/DocumentView.tsx            |  4 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  5 +-
 src/client/views/nodes/ImageBox.tsx                |  6 +--
 src/client/views/nodes/VideoBox.tsx                |  4 +-
 src/client/views/pdf/PDFViewer.tsx                 | 55 +++++-----------------
 src/client/views/search/SearchBox.tsx              |  6 +--
 src/mobile/ImageUpload.tsx                         |  5 +-
 .../authentication/models/current_user_utils.ts    |  5 +-
 src/server/index.ts                                |  2 +-
 19 files changed, 65 insertions(+), 81 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index e8a80bdc3..ac6d127cc 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -2,6 +2,7 @@ import v4 = require('uuid/v4');
 import v5 = require("uuid/v5");
 import { Socket } from 'socket.io';
 import { Message } from './server/Message';
+import { RouteStore } from './server/RouteStore';
 
 export class Utils {
 
@@ -27,6 +28,18 @@ export class Utils {
         return { scale, translateX, translateY };
     }
 
+    /**
+     * A convenience method. Prepends the full path (i.e. http://localhost:1050) to the
+     * requested extension
+     * @param extension the specified sub-path to append to the window origin
+     */
+    public static prepend(extension: string): string {
+        return window.location.origin + extension;
+    }
+    public static CorsProxy(url: string): string {
+        return this.prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
+    }
+
     public static CopyText(text: string) {
         var textArea = document.createElement("textarea");
         textArea.value = text;
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 6737657c8..8c64d2b2f 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -47,14 +47,6 @@ export namespace DocServer {
         Utils.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete);
         Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete);
     }
-    /**
-     * A convenience method. Prepends the full path (i.e. http://localhost:1050) to the
-     * requested extension
-     * @param extension the specified sub-path to append to the window origin
-     */
-    export function prepend(extension: string): string {
-        return window.location.origin + extension;
-    }
 
     function errorFunc(): never {
         throw new Error("Can't use DocServer without calling init first");
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index cbf5b3fc8..e9ff21b22 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -129,7 +129,7 @@ export namespace HistoryUtil {
 
     function addStringifier(type: string, keys: string[], customStringifier?: (state: ParsedUrl, current: string) => string) {
         stringifiers[type] = state => {
-            let path = DocServer.prepend(`/${type}`);
+            let path = Utils.prepend(`/${type}`);
             if (customStringifier) {
                 path = customStringifier(state, path);
             }
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index c096e9ceb..75b0b52a7 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -98,12 +98,12 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
             runInAction(() => this.remaining++);
 
-            let prom = fetch(DocServer.prepend(RouteStore.upload), {
+            let prom = fetch(Utils.prepend(RouteStore.upload), {
                 method: 'POST',
                 body: formData
             }).then(async (res: Response) => {
                 (await res.json()).map(action((file: any) => {
-                    let docPromise = Docs.Get.DocumentFromType(type, DocServer.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName });
+                    let docPromise = Docs.Get.DocumentFromType(type, Utils.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName });
                     docPromise.then(doc => {
                         doc && docs.push(doc) && runInAction(() => this.remaining--);
                     });
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index abf1a7c32..ee5a83710 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -2,6 +2,7 @@ import * as rp from 'request-promise';
 import { DocServer } from '../DocServer';
 import { Doc } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
+import { Utils } from '../../Utils';
 
 export namespace SearchUtil {
     export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
@@ -29,7 +30,7 @@ export namespace SearchUtil {
     export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
     export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
         query = query || "*"; //If we just have a filter query, search for * as the query
-        const result: IdSearchResult = JSON.parse(await rp.get(DocServer.prepend("/search"), {
+        const result: IdSearchResult = JSON.parse(await rp.get(Utils.prepend("/search"), {
             qs: { ...options, q: query },
         }));
         if (!returnDocs) {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 309d19016..a4c053de2 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -19,7 +19,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
 import { DocumentManager } from "./DocumentManager";
 import { Id } from "../../new_fields/FieldSymbols";
 import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
-import { SelectionManager } from "./SelectionManager";
+import { Utils } from "../../Utils";
 
 //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
 export class TooltipTextMenu {
@@ -213,8 +213,8 @@ export class TooltipTextMenu {
                 let link = node && node.marks.find(m => m.type.name === "link");
                 if (link) {
                     let href: string = link.attrs.href;
-                    if (href.indexOf(DocServer.prepend("/doc/")) === 0) {
-                        let docid = href.replace(DocServer.prepend("/doc/"), "");
+                    if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+                        let docid = href.replace(Utils.prepend("/doc/"), "");
                         DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
                             if (f instanceof Doc) {
                                 if (DocumentManager.Instance.getDocumentView(f)) {
@@ -254,7 +254,7 @@ export class TooltipTextMenu {
                                 if (docView && docView.props.ContainingCollectionView) {
                                     proto.sourceContext = docView.props.ContainingCollectionView.props.Document;
                                 }
-                                linkDoc instanceof Doc && this.makeLink(DocServer.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab");
+                                linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab");
                             }),
                         },
                         hideSource: false
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ce7369220..94a4835a1 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -16,7 +16,7 @@ import { listSpec } from '../../new_fields/Schema';
 import { Cast, FieldValue, NumCast, BoolCast, StrCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnOne, returnTrue } from '../../Utils';
+import { emptyFunction, returnOne, returnTrue, Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
 import { Docs } from '../documents/Documents';
 import { SetupDrag } from '../util/DragManager';
@@ -433,7 +433,7 @@ export class MainView extends React.Component {
         return [
             this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
             <div className="main-buttonDiv" key="logout" style={{ bottom: '0px', right: '1px', position: 'absolute' }} ref={logoutRef}>
-                <button onClick={() => window.location.assign(DocServer.prepend(RouteStore.logout))}>Log Out</button></div>
+                <button onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>Log Out</button></div>
         ];
 
     }
diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx
index 1b9be841f..33cb63df5 100644
--- a/src/client/views/SearchBox.tsx
+++ b/src/client/views/SearchBox.tsx
@@ -13,6 +13,7 @@ import { Docs } from '../documents/Documents';
 import { SetupDrag } from '../util/DragManager';
 import { SearchItem } from './search/SearchItem';
 import "./SearchBox.scss";
+import { Utils } from '../../Utils';
 
 library.add(faSearch);
 library.add(faObjectGroup);
@@ -47,7 +48,7 @@ export class SearchBox extends React.Component {
 
     @action
     getResults = async (query: string) => {
-        let response = await rp.get(DocServer.prepend('/search'), {
+        let response = await rp.get(Utils.prepend('/search'), {
             qs: {
                 query
             }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index a193ff677..ba7903419 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -465,7 +465,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
             .off('click') //unbind the current click handler
             .click(action(function () {
                 stack.config.fixed = !stack.config.fixed;
-                // var url = DocServer.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
+                // var url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
                 // let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
             }));
     }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5d5189336..2ddefb3c0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -20,6 +20,7 @@ import { CollectionVideoView } from "./CollectionVideoView";
 import { CollectionView } from "./CollectionView";
 import React = require("react");
 import { MainView } from "../MainView";
+import { Utils } from "../../../Utils";
 
 export interface CollectionViewProps extends FieldViewProps {
     addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -164,7 +165,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 } else {
                     let path = window.location.origin + "/doc/";
                     if (text.startsWith(path)) {
-                        let docid = text.replace(DocServer.prepend("/doc/"), "").split("?")[0];
+                        let docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
                         DocServer.GetRefField(docid).then(f => {
                             if (f instanceof Doc) {
                                 if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
@@ -193,7 +194,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
                     let str: string;
                     let prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
-                        .then(action((s: string) => rp.head(DocServer.prepend(RouteStore.corsProxy + "/" + (str = s)))))
+                        .then(action((s: string) => rp.head(Utils.CorsProxy(str = s))))
                         .then(result => {
                             let type = result["content-type"];
                             if (type) {
@@ -219,7 +220,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                     }).then(async (res: Response) => {
                         (await res.json()).map(action((file: any) => {
                             let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
-                            let path = DocServer.prepend(file);
+                            let path = Utils.prepend(file);
                             Docs.Get.DocumentFromType(type, path, full).then(doc => doc && this.props.addDocument(doc));
                         }));
                     });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 245dd319d..af1cc079b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -557,13 +557,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         });
         cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
         cm.addItem({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
-        cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" });
+        cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" });
         cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
         cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
         type User = { email: string, userDocumentId: string };
         let usersMenu: ContextMenuProps[] = [];
         try {
-            let stuff = await rp.get(DocServer.prepend(RouteStore.getUsers));
+            let stuff = await rp.get(Utils.prepend(RouteStore.getUsers));
             const users: User[] = JSON.parse(stuff);
             usersMenu = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({
                 description: email, event: async () => {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 066cc40e2..99801ecff 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -35,6 +35,7 @@ import "./FormattedTextBox.scss";
 import React = require("react");
 import { DateField } from '../../../new_fields/DateField';
 import { thisExpression } from 'babel-types';
+import { Utils } from '../../../Utils';
 
 library.add(faEdit);
 library.add(faSmile);
@@ -311,8 +312,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 href = parent.childNodes[0].href ? parent.childNodes[0].href : parent.href;
             }
             if (href) {
-                if (href.indexOf(DocServer.prepend("/doc/")) === 0) {
-                    this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0];
+                if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+                    this._linkClicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
                     if (this._linkClicked) {
                         DocServer.GetRefField(this._linkClicked).then(async linkDoc => {
                             if (linkDoc instanceof Doc) {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ebe3732e6..4ffd265d7 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -165,12 +165,12 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
             recorder.ondataavailable = async function (e: any) {
                 const formData = new FormData();
                 formData.append("file", e.data);
-                const res = await fetch(DocServer.prepend(RouteStore.upload), {
+                const res = await fetch(Utils.prepend(RouteStore.upload), {
                     method: 'POST',
                     body: formData
                 });
                 const files = await res.json();
-                const url = DocServer.prepend(files[0]);
+                const url = Utils.prepend(files[0]);
                 // upload to server with known URL 
                 let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", x: NumCast(self.props.Document.x), y: NumCast(self.props.Document.y), width: 200, height: 32 });
                 audioDoc.embed = true;
@@ -327,7 +327,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx
         let nativeWidth = FieldValue(this.Document.nativeWidth, pw);
         let nativeHeight = FieldValue(this.Document.nativeHeight, 0);
-        let paths: string[] = [window.origin + RouteStore.corsProxy + "/" + "http://www.cs.brown.edu/~bcz/noImage.png"];
+        let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
         // this._curSuffix = "";
         // if (w > 20) {
         Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 8b8e500c4..0f2d18f6b 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -122,7 +122,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
 
     public static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
-            let posting = DocServer.prepend(RouteStore.dataUriToImage);
+            let posting = Utils.prepend(RouteStore.dataUriToImage);
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
@@ -164,7 +164,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
                     let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, "");
                     VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
                         if (returnedFilename) {
-                            let url = DocServer.prepend(returnedFilename);
+                            let url = Utils.prepend(returnedFilename);
                             let imageSummary = Docs.Create.ImageDocument(url, {
                                 x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y),
                                 width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-"
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 699a1ffd7..b7ded7e06 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,23 +4,18 @@ import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
 import * as rp from "request-promise";
 import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
 import { Id } from "../../../new_fields/FieldSymbols";
 import { List } from "../../../new_fields/List";
-import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../new_fields/Types";
-import { emptyFunction } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-import { Docs, DocUtils, DocumentOptions } from "../../documents/Documents";
-import { DocumentManager } from "../../util/DocumentManager";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { emptyFunction, Utils } from "../../../Utils";
+import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
-import { DocumentView } from "../nodes/DocumentView";
-import { PDFBox, handleBackspace } from "../nodes/PDFBox";
+import { PDFBox } from "../nodes/PDFBox";
 import Page from "./Page";
 import "./PDFViewer.scss";
 import React = require("react");
-import PDFMenu from "./PDFMenu";
-import { UndoManager } from "../../util/UndoManager";
-import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
+import { CompileScript, CompileResult } from "../../util/Scripting";
 import { ScriptField } from "../../../new_fields/ScriptField";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import Annotation from "./Annotation";
@@ -90,16 +85,12 @@ export class Viewer extends React.Component<IViewerProps> {
     private _annotationReactionDisposer?: IReactionDisposer;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _filterReactionDisposer?: IReactionDisposer;
-    private _activeReactionDisposer?: IReactionDisposer;
     private _viewer: React.RefObject<HTMLDivElement>;
     private _mainCont: React.RefObject<HTMLDivElement>;
     private _pdfViewer: any;
     // private _textContent: Pdfjs.TextContent[] = [];
     private _pdfFindController: any;
     private _searchString: string = "";
-    private _rendered: boolean = false;
-    private _pageIndex: number = -1;
-    private _matchIndex: number = 0;
 
     constructor(props: IViewerProps) {
         super(props);
@@ -135,23 +126,6 @@ export class Viewer extends React.Component<IViewerProps> {
             },
             { fireImmediately: true });
 
-        this._activeReactionDisposer = reaction(
-            () => this.props.parent.props.active(),
-            () => {
-                runInAction(() => {
-                    if (!this.props.parent.props.active()) {
-                        this._searching = false;
-                        this._pdfFindController = null;
-                        if (this._viewer.current) {
-                            let cns = this._viewer.current.childNodes;
-                            for (let i = cns.length - 1; i >= 0; i--) {
-                                cns.item(i).remove();
-                            }
-                        }
-                    }
-                });
-            }
-        );
 
         if (this.props.parent.props.ContainingCollectionView) {
             this._filterReactionDisposer = reaction(
@@ -346,7 +320,7 @@ export class Viewer extends React.Component<IViewerProps> {
             this._isPage[page] = "image";
             const address = this.props.url;
             try {
-                let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
+                let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
                 runInAction(() => this._visibleElements[page] =
                     <img key={res.path} src={res.path} onError={handleError}
                         style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
@@ -476,7 +450,6 @@ export class Viewer extends React.Component<IViewerProps> {
                             phraseSearch: true,
                             query: searchString
                         });
-                    this._rendered = true;
                 });
                 container.addEventListener("pagerendered", () => {
                     console.log("rendered");
@@ -488,7 +461,6 @@ export class Viewer extends React.Component<IViewerProps> {
                             phraseSearch: true,
                             query: searchString
                         });
-                    this._rendered = true;
                 });
             }
         }
@@ -563,7 +535,6 @@ export class Viewer extends React.Component<IViewerProps> {
                     });
                     container.addEventListener("pagerendered", () => {
                         console.log("rendered");
-                        this._rendered = true;
                     });
                     this._pdfViewer.setDocument(this.props.pdf);
                     this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer);
@@ -703,17 +674,17 @@ class SimpleLinkService {
     externalLinkRel: any = null;
     pdf: any = null;
 
-    navigateTo(dest: any) { }
+    navigateTo() { }
 
-    getDestinationHash(dest: any) { return "#"; }
+    getDestinationHash() { return "#"; }
 
-    getAnchorUrl(hash: any) { return "#"; }
+    getAnchorUrl() { return "#"; }
 
-    setHash(hash: any) { }
+    setHash() { }
 
-    executeNamedAction(action: any) { }
+    executeNamedAction() { }
 
-    cachePageRef(pageNum: any, pageRef: any) { }
+    cachePageRef() { }
 
     get pagesCount() {
         return this.pdf ? this.pdf.numPages : 0;
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 16c44225a..2214ac8af 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -9,12 +9,12 @@ import { Docs } from '../../documents/Documents';
 import { NumCast, Cast } from '../../../new_fields/Types';
 import { Doc } from '../../../new_fields/Doc';
 import { SearchItem } from './SearchItem';
-import { DocServer } from '../../DocServer';
 import * as rp from 'request-promise';
 import { Id } from '../../../new_fields/FieldSymbols';
 import { SearchUtil } from '../../util/SearchUtil';
 import { RouteStore } from '../../../server/RouteStore';
 import { FilterBox } from './FilterBox';
+import { Utils } from '../../../Utils';
 
 
 @observer
@@ -74,7 +74,7 @@ export class SearchBox extends React.Component {
 
     public static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
-            let posting = DocServer.prepend(RouteStore.dataUriToImage);
+            let posting = Utils.prepend(RouteStore.dataUriToImage);
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
@@ -154,7 +154,7 @@ export class SearchBox extends React.Component {
                         filteredDocs.forEach(doc => {
                             const index = this._resultsSet.get(doc);
                             const highlight = highlights[doc[Id]];
-                            const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)) : []
+                            const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)) : [];
                             if (index === undefined) {
                                 this._resultsSet.set(doc, this._results.length);
                                 this._results.push([doc, hlights]);
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index a8f94b746..33a615cbf 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -11,6 +11,7 @@ import { listSpec } from '../new_fields/Schema';
 import { List } from '../new_fields/List';
 import { observer } from 'mobx-react';
 import { observable } from 'mobx';
+import { Utils } from '../Utils';
 
 
 
@@ -57,7 +58,7 @@ class Uploader extends React.Component {
 
                         this.status = "getting user document";
 
-                        const res = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId));
+                        const res = await rp.get(Utils.prepend(RouteStore.getUserDocumentId));
                         if (!res) {
                             throw new Error("No user id returned");
                         }
@@ -104,6 +105,8 @@ class Uploader extends React.Component {
 }
 
 
+DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload");
+
 ReactDOM.render((
     <Uploader />
 ),
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index e796ccb43..1c52a3f11 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -12,6 +12,7 @@ import { List } from "../../../new_fields/List";
 import { listSpec } from "../../../new_fields/Schema";
 import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
 import { RouteStore } from "../../RouteStore";
+import { Utils } from "../../../Utils";
 
 export class CurrentUserUtils {
     private static curr_email: string;
@@ -74,7 +75,7 @@ export class CurrentUserUtils {
     }
 
     public static loadCurrentUser() {
-        return rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => {
+        return rp.get(Utils.prepend(RouteStore.getCurrUser)).then(response => {
             if (response) {
                 const result: { id: string, email: string } = JSON.parse(response);
                 return result;
@@ -87,7 +88,7 @@ export class CurrentUserUtils {
     public static async loadUserDocument({ id, email }: { id: string, email: string }) {
         this.curr_id = id;
         this.curr_email = email;
-        await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => {
+        await rp.get(Utils.prepend(RouteStore.getUserDocumentId)).then(id => {
             if (id) {
                 return DocServer.GetRefField(id).then(async field => {
                     if (field instanceof Doc) {
diff --git a/src/server/index.ts b/src/server/index.ts
index b3859aa4f..6b4e59bfc 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -417,7 +417,7 @@ app.get(RouteStore.reset, getReset);
 app.post(RouteStore.reset, postReset);
 
 app.use(RouteStore.corsProxy, (req, res) =>
-    req.pipe(request(req.url.substring(1))).pipe(res));
+    req.pipe(request(decodeURIComponent(req.url.substring(1)))).pipe(res));
 
 app.get(RouteStore.delete, (req, res) => {
     if (release) {
-- 
cgit v1.2.3-70-g09d2


From d03fc0c58b0a760bdef17b4e6ef7c59c96562521 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 17 Jul 2019 22:32:59 -0400
Subject: fixes for audio annotations

---
 src/client/views/nodes/ImageBox.scss | 18 ++++++++++++++++++
 src/client/views/nodes/ImageBox.tsx  | 25 +++++++++++--------------
 2 files changed, 29 insertions(+), 14 deletions(-)

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

diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index f1b73a676..697f19f0d 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -38,4 +38,22 @@
   border: none;
   width: 100%;
   height: 100%;
+}
+
+.imageBox-audioBackground {
+    display: inline-block;
+    width: 10%;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    border-radius: 25px;
+    background: white;
+    opacity: 0.3;
+    svg {
+        width: 90% !important;
+        height: 70%;
+        position: absolute;
+        left: 5%;
+        top: 15%;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ebe3732e6..7daed86f8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -284,15 +284,17 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let self = this;
         let audioAnnos = DocListCast(this.extensionDoc.audioAnnotations);
         if (audioAnnos.length && this._audioState === 0) {
-            audioAnnos.map(anno => anno.data instanceof AudioField && new Howl({
+            let anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
+            anno.data instanceof AudioField && new Howl({
                 src: [anno.data.url.href],
+                format: ["mp3"],
                 autoplay: true,
                 loop: false,
                 volume: 0.5,
                 onend: function () {
                     runInAction(() => self._audioState = 0);
                 }
-            }));
+            });
             this._audioState = 1;
         }
         // else {
@@ -350,28 +352,23 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
 
         return (
             <div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
-                onPointerEnter={this.onPointerEnter}
                 onPointerDown={this.onPointerDown}
                 onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
                 <img id={id}
                     key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
                     src={srcpath}
                     style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
-                    // style={{ objectFit: (this.Document.curPage === 0 ? undefined : "contain") }}
                     width={nativeWidth}
                     ref={this._imgRef}
                     onError={this.onError} />
                 {paths.length > 1 ? this.dots(paths) : (null)}
-                <div onPointerDown={this.audioDown} style={{
-                    display: DocListCast(this.extensionDoc.audioAnnotations).length ? "inline" : "inline",
-                    width: nativeWidth * 0.1,
-                    height: nativeWidth * 0.1,
-                    position: "absolute",
-                    top: 0,
-                    left: 0,
-                }}>
-                    <FontAwesomeIcon
-                        style={{ width: "100%", height: "100%", color: [DocListCast(this.extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={faFileAudio} size="sm" />
+                <div className="imageBox-audioBackground"
+                    onPointerDown={this.audioDown}
+                    onPointerEnter={this.onPointerEnter}
+                    style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }}
+                >
+                    <FontAwesomeIcon className="imageBox-audioFont"
+                        style={{ color: [DocListCast(this.extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={faFileAudio} size="sm" />
                 </div>
                 {/* {this.lightbox(paths)} */}
             </div>);
-- 
cgit v1.2.3-70-g09d2

