From ded69655dabf97c76f97271e7da8e77e3f33ec25 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 15 Mar 2021 03:36:35 -0400
Subject: getting webBox's to work like PdfBox's - text selection & marquee.

---
 src/client/views/nodes/WebBox.tsx | 158 +++++++++++++++++++++++++++++++-------
 1 file changed, 131 insertions(+), 27 deletions(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 6127f82e3..9e6e94248 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -37,6 +37,7 @@ import { CollectionStackingView } from "../collections/CollectionStackingView";
 import { StyleProp } from "../StyleProvider";
 import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import { CollectionViewType } from "../collections/CollectionView";
+import { AnchorMenu } from "../pdf/AnchorMenu";
 const htmlToText = require("html-to-text");
 
 type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -64,6 +65,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
     get scrollHeight() { return this.webpage?.scrollHeight || 1000; }
     get webpage() { return this._iframe?.contentDocument?.children[0]; }
+    @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
 
     constructor(props: any) {
         super(props);
@@ -71,12 +73,114 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850);
             Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850);
         }
-        this._annotationKey = this._annotationKey + "-" + this.urlHash(this._url);
+        if (this.layoutDoc[this.fieldKey + "-contentWidth"] === undefined) {
+            this.layoutDoc[this.fieldKey + "-contentWidth"] = Doc.NativeWidth(this.layoutDoc);
+        }
+        this._annotationKey = "annotations-" + this.urlHash(this._url);
+    }
+
+    @action
+    createTextAnnotation = (sel: Selection, selRange: Range) => {
+        if (this._mainCont.current) {
+            const clientRects = selRange.getClientRects();
+            for (let i = 0; i < clientRects.length; i++) {
+                const rect = clientRects.item(i);
+                if (rect && rect.width !== this._mainCont.current.clientWidth) {
+                    const annoBox = document.createElement("div");
+                    annoBox.className = "marqueeAnnotator-annotationBox";
+                    // transforms the positions from screen onto the pdf div
+                    annoBox.style.top = (rect.top + this._mainCont.current.scrollTop).toString();
+                    annoBox.style.left = (rect.left).toString();
+                    annoBox.style.width = (rect.width).toString();
+                    annoBox.style.height = (rect.height).toString();
+                    this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1);
+                }
+            }
+        }
+        //this._selectionText = selRange.cloneContents().textContent || "";
+
+        // clear selection
+        if (sel.empty) {  // Chrome
+            sel.empty();
+        } else if (sel.removeAllRanges) {  // Firefox
+            sel.removeAllRanges();
+        }
+    }
+
+    @action
+    ptrUp = (e: PointerEvent) => {
+        if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
+            this._iframe.contentDocument.addEventListener("pointerup", this.ptrUp);
+            const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
+            const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+            const sel = this._iframe.contentWindow.getSelection();
+            if (sel) {
+                this.createTextAnnotation(sel, sel.getRangeAt(0))
+                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX,
+                    e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
+            }
+        } else AnchorMenu.Instance.fadeOut(true);
+    }
+
+    getWordAtPoint = (elem: any, x: number, y: number): Opt<string> => {
+        if (elem.nodeType == elem.TEXT_NODE) {
+            var range = elem.ownerDocument.createRange();
+            range.selectNodeContents(elem);
+            var currentPos = 0;
+            var endPos = range.endOffset;
+            while (currentPos + 1 < endPos) {
+                range.setStart(elem, currentPos);
+                range.setEnd(elem, currentPos + 1);
+                const rangeRect = range.getBoundingClientRect();
+                if (rangeRect.left <= x && rangeRect.right >= x &&
+                    rangeRect.top <= y && rangeRect.bottom >= y) {
+                    range.expand("word");
+                    var ret = range.toString();
+                    range.detach();
+                    return (ret);
+                }
+                currentPos += 1;
+            }
+        } else {
+            for (var i = 0; i < elem.childNodes.length; i++) {
+                var range = elem.childNodes[i].ownerDocument.createRange();
+                range.selectNodeContents(elem.childNodes[i]);
+                const rangeRect = range.getBoundingClientRect();
+                if (rangeRect.left <= x && rangeRect.right >= x &&
+                    rangeRect.top <= y && rangeRect.bottom >= y) {
+                    range.detach();
+                    const word = this.getWordAtPoint(elem.childNodes[i], x, y);
+                    if (word) return word;
+                } else {
+                    range.detach();
+                }
+            }
+        }
+        return undefined;
+    }
+    @action
+    ptrDown = (e: PointerEvent) => {
+        const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
+        const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
+        const word = this.getWordAtPoint(e.target, e.clientX, e.clientY);
+        this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
+        e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
+        if (word) {
+            console.log(word);
+            this._iframe?.contentDocument?.addEventListener("pointerup", this.ptrUp);
+            setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+        } else {
+            this.isAnnotating = true;
+            this.props.select(false);
+            e.stopPropagation();
+            e.preventDefault();
+        }
     }
 
     iframeLoaded = (e: any) => {
         const iframe = this._iframe;
         if (iframe?.contentDocument) {
+            iframe?.contentDocument.addEventListener("pointerdown", this.ptrDown);
             if (this._initialScroll !== undefined && this._outerRef.current && this.webpage) {
                 this.webpage.scrollTop = this._initialScroll;
                 this._outerRef.current.scrollTop = this._initialScroll;
@@ -134,6 +238,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     onScroll = (e: any) => {
         if (!this._ignoreScroll.includes("iframe") && this.webpage) {
             this.webpage.scrollTop = this._outerRef.current?.scrollTop || 0;
+        }
+        if (this._ignoreScroll !== "iframe|outer") {
             this.layoutDoc._scrollTop = this._outerRef.current?.scrollTop;
         }
     }
@@ -258,7 +364,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         if (future.length) {
             history.push(this._url);
             this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!));
-            this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url);
+            this._annotationKey = "annotations-" + this.urlHash(this._url);
             return true;
         }
         return false;
@@ -272,7 +378,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
             else future.push(this._url);
             this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!));
-            this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url);
+            this._annotationKey = "annotations-" + this.urlHash(this._url);
             return true;
         }
         return false;
@@ -299,7 +405,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 future && (future.length = 0);
             }
             this._url = newUrl;
-            this._annotationKey = this.fieldKey + "-annotations-" + this.urlHash(this._url);
+            this._annotationKey = "annotations-" + this.urlHash(this._url);
             this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl));
         } catch (e) {
             console.log("WebBox URL error:" + this._url);
@@ -353,16 +459,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         );
     }
 
-    editToggleBtn() {
-        return <Tooltip title={<div className="dash-tooltip" >{`${this.props.Document.isAnnotating ? "Exit" : "Enter"} annotation mode`}</div>}>
-            <div className="webBox-annotationToggle"
-                style={{ color: this.props.Document.isAnnotating ? "black" : "white", backgroundColor: this.props.Document.isAnnotating ? "white" : "black" }}
-                onClick={action(() => this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating)}>
-                <FontAwesomeIcon icon="edit" size="sm" />
-            </div>
-        </Tooltip>;
-    }
-
     _ignore = 0;
     onPreWheel = (e: React.WheelEvent) => this._ignore = e.timeStamp;
     onPrePointer = (e: React.PointerEvent) => this._ignore = e.timeStamp;
@@ -583,7 +679,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 style={{
                     width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / scale}%`,
                     height: `${100 / scale}%`,
-                    transform: `scale(${scale})`
                 }}
                 onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
                 {this.urlContent}
@@ -599,13 +694,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         </>);
     }
 
-    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]); }
-    @computed get nonDocAnnotations() { return this.allAnnotations.filter(a => a.annotations); }
+    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
+    @observable private _overlayAnnoInfo: Opt<Doc>;
+    @computed get allAnnotations() {
+        return DocListCast(this.dataDoc[this.annotationKey]);
+    }
     @computed get annotationLayer() {
         TraceMobx();
         return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
-            {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation {...this.props} showInfo={emptyFunction} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
+            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
+                <Annotation {...this.props} fieldKey={this.annotationKey} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)
             }
         </div>;
     }
@@ -617,9 +715,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             this.props.select(false);
         }
     }
+    @observable isAnnotating = false;
 
     @action
-    finishMarquee = () => this._marqueeing = undefined;
+    finishMarquee = () => {
+        this._marqueeing = undefined;
+        this.isAnnotating = false;
+    }
 
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
@@ -634,11 +736,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     onWheel={this.onWebWheel}
                     onContextMenu={this.specificContextMenu}>
                     <base target="_blank" />
-                    {this.content}
                     <div className={"webBox-outerContent"} ref={this._outerRef}
                         style={{
-                            width: `calc(${100 / scale}% - ${this.sidebarWidth()}px)`, height: `${100 / scale}%`, transform: `scale(${scale})`,
-                            pointerEvents: !this.layoutDoc.isAnnotating || inactiveLayer ? "none" : "all"
+                            width: `calc(${100 / scale}% - ${this.sidebarWidth()}px)`,
+                            height: `${100 / scale}%`,
+                            transform: `scale(${scale})`,
+                            pointerEvents: inactiveLayer ? "none" : "all"
                         }}
                         onWheel={this.onWheel}
                         onPointerDown={this.onMarqueeDown}
@@ -648,12 +751,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             height: NumCast(this.scrollHeight, 50),
                             pointerEvents: inactiveLayer ? "none" : undefined
                         }}>
+                            {this.content}
                             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                                 renderDepth={this.props.renderDepth + 1}
                                 CollectionView={undefined}
                                 fieldKey={this.annotationKey}
                                 isAnnotationOverlay={true}
                                 scaling={returnOne}
+                                pointerEvents={this.isAnnotating ? "all" : "none"}
                                 PanelWidth={this.panelWidth}
                                 PanelHeight={this.panelHeight}
                                 ScreenToLocalTransform={this.scrollXf}
@@ -663,14 +768,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                                 select={emptyFunction}
                                 active={this.active}
                                 whenActiveChanged={this.whenActiveChanged} />
+                            {this.annotationLayer}
                         </div>
                     </div>
-                    {this.annotationLayer}
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
                         <MarqueeAnnotator rootDoc={this.rootDoc}
                             anchorMenuClick={this.anchorMenuClick}
-                            scrollTop={NumCast(this.rootDoc._scrollTop)}
-                            down={this._marqueeing} scaling={this.props.scaling}
+                            scrollTop={0}
+                            down={this._marqueeing} scaling={returnOne}
                             addDocument={this.addDocument}
                             finishMarquee={this.finishMarquee}
                             savedAnnotations={this._savedAnnotations}
@@ -682,7 +787,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     <FontAwesomeIcon style={{ color: "white" }} icon={"chevron-left"} size="sm" />
                 </button>
                 {this.sidebarOverlay}
-                {this.props.isSelected() ? this.editToggleBtn() : null}
             </div>);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 363b73726523caff1e5f047287c7fdb242b39e20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 15 Mar 2021 13:19:24 -0400
Subject: fixed scrolling to annotations and pointerevents on webBox (when an
 annotation was selected so you can drag out a marquee)

---
 src/Utils.ts                                       |  13 +--
 src/client/views/MarqueeAnnotator.tsx              |   5 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 src/client/views/nodes/WebBox.tsx                  | 122 ++++++---------------
 4 files changed, 41 insertions(+), 101 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index f22df0da2..936a459ba 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -482,7 +482,7 @@ const easeInOutQuad = (currentTime: number, start: number, change: number, durat
     return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start;
 };
 
-export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number, finish?: () => void, reset?: { resetGoTo: { to: number, duration: number } | undefined }) {
+export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number) {
     const elements = (element instanceof HTMLElement ? [element] : element);
     let starts = elements.map(element => element.scrollTop);
     let startDate = new Date().getTime();
@@ -490,23 +490,12 @@ export function smoothScroll(duration: number, element: HTMLElement | HTMLElemen
     const animateScroll = () => {
         const currentDate = new Date().getTime();
         let currentTime = currentDate - startDate;
-        const resetParams = reset?.resetGoTo;
-        if (resetParams) {
-            reset!.resetGoTo = undefined;
-            const { to: newTo, duration: newDuration } = resetParams;
-            to = newTo;
-            starts = starts.map(start => easeInOutQuad(currentTime, start, to - start, duration));
-            startDate = currentDate;
-            duration = newDuration;
-            currentTime = currentDate - startDate;
-        }
         elements.map((element, i) => element.scrollTop = easeInOutQuad(currentTime, starts[i], to - starts[i], duration));
 
         if (currentTime < duration) {
             requestAnimationFrame(animateScroll);
         } else {
             elements.forEach(element => element.scrollTop = to);
-            finish?.();
         }
     };
     animateScroll();
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index c43dd6ba8..59500070c 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -30,7 +30,7 @@ export interface MarqueeAnnotatorProps {
     annotationLayer: HTMLDivElement;
     addDocument: (doc: Doc) => boolean;
     getPageFromScroll?: (top: number) => number;
-    finishMarquee: () => void;
+    finishMarquee: (x?: number, y?: number) => void;
     anchorMenuClick?: (anchor: Doc) => void;
 }
 @observer
@@ -204,10 +204,11 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             if (AnchorMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
                 this.highlight("rgba(245, 230, 95, 0.75)", false);  // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
             }
+            this.props.finishMarquee();
         } else {
             runInAction(() => this._width = this._height = 0);
+            this.props.finishMarquee(e.clientX, e.clientY);
         }
-        this.props.finishMarquee();
     }
 
     render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index daa92055c..b5dca42a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1493,7 +1493,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             onDragOver={e => e.preventDefault()}
             onContextMenu={this.onContextMenu}
             style={{
-                pointerEvents: this.backgroundEvents ? "all" : this.props.pointerEvents,
+                pointerEvents: this.backgroundEvents ? "all" : this.props.pointerEvents as any,
                 transform: `scale(${this.contentScaling || 1})`,
                 width: `${100 / (this.contentScaling || 1)}%`,
                 height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 9e6e94248..0f10d7cae 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,10 +1,9 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Dictionary } from "typescript-collections";
 import * as WebRequest from 'web-request';
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
 import { HtmlField } from "../../../fields/HtmlField";
@@ -14,30 +13,30 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { WebField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, returnOne, smoothScroll, Utils, returnZero, returnTrue } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTrue, returnZero, smoothScroll, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
+import { DocumentType } from '../../documents/DocumentTypes';
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DragManager } from "../../util/DragManager";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
+import { CollectionStackingView } from "../collections/CollectionStackingView";
+import { CollectionViewType } from "../collections/CollectionView";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
 import { MarqueeAnnotator } from "../MarqueeAnnotator";
+import { AnchorMenu } from "../pdf/AnchorMenu";
 import { Annotation } from "../pdf/Annotation";
+import { SearchBox } from "../search/SearchBox";
+import { StyleProp } from "../StyleProvider";
 import { FieldView, FieldViewProps } from './FieldView';
+import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import { LinkDocPreview } from "./LinkDocPreview";
 import "./WebBox.scss";
-import { DocumentType } from '../../documents/DocumentTypes';
 import React = require("react");
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SearchBox } from "../search/SearchBox";
-import { CollectionStackingView } from "../collections/CollectionStackingView";
-import { StyleProp } from "../StyleProvider";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
-import { CollectionViewType } from "../collections/CollectionView";
-import { AnchorMenu } from "../pdf/AnchorMenu";
 const htmlToText = require("html-to-text");
 
 type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -47,20 +46,21 @@ const WebDocument = makeInterface(documentSchema);
 export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+    private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _longPressSecondsHack?: NodeJS.Timeout;
-    private _outerRef = React.createRef<HTMLDivElement>();
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
     private _iframeDragRef = React.createRef<HTMLDivElement>();
     private _keyInput = React.createRef<HTMLInputElement>();
-    private _ignoreScroll = "";
     private _scrollTimer: any;
     private _initialScroll: Opt<number>;
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
     @observable private _marqueeing: number[] | undefined;
     @observable private _url: string = "hello";
     @observable private _pressX: number = 0;
     @observable private _pressY: number = 0;
+    @observable private _isAnnotating = false;
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
     get scrollHeight() { return this.webpage?.scrollHeight || 1000; }
@@ -121,7 +121,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             }
         } else AnchorMenu.Instance.fadeOut(true);
     }
-
     getWordAtPoint = (elem: any, x: number, y: number): Opt<string> => {
         if (elem.nodeType == elem.TEXT_NODE) {
             var range = elem.ownerDocument.createRange();
@@ -166,11 +165,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
         e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
         if (word) {
-            console.log(word);
             this._iframe?.contentDocument?.addEventListener("pointerup", this.ptrUp);
             setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
         } else {
-            this.isAnnotating = true;
+            this._isAnnotating = true;
             this.props.select(false);
             e.stopPropagation();
             e.preventDefault();
@@ -181,8 +179,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         const iframe = this._iframe;
         if (iframe?.contentDocument) {
             iframe?.contentDocument.addEventListener("pointerdown", this.ptrDown);
-            if (this._initialScroll !== undefined && this._outerRef.current && this.webpage) {
-                this.webpage.scrollTop = this._initialScroll;
+            if (this._initialScroll !== undefined && this._outerRef.current) {
                 this._outerRef.current.scrollTop = this._initialScroll;
                 this._initialScroll = undefined;
             }
@@ -201,65 +198,27 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 }
             })));
             iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
-            iframe.contentDocument.addEventListener('scroll', this.iframeScroll, false);
         }
     }
 
-    resetIgnoreScroll = () => {
+    resetIgnoreScroll = (timeout: number = 250) => {
         this._scrollTimer && clearTimeout(this._scrollTimer);
         this._scrollTimer = setTimeout(() => {
             this._scrollTimer = undefined;
-            this._ignoreScroll = "";
-        }, 250);
-        this._outerRef.current && (this._outerRef.current.scrollLeft = 0);
-    }
-    iframeWheel = (e: any) => {
-        this._ignoreScroll = "iframe";
-        this.resetIgnoreScroll();
-        e.stopPropagation();
-    }
-    onWebWheel = (e: React.WheelEvent) => {
-        this._ignoreScroll = "iframe";
-        this.goTo(Math.max(0, (this.webpage?.scrollTop || 0) + (this._accumulatedGoTo + 1) * e.deltaY), 100);
-        this.resetIgnoreScroll();
-        e.stopPropagation();
-    }
-    onWheel = (e: React.WheelEvent) => {
-        this._ignoreScroll = "outer";
-        this.resetIgnoreScroll();
-        e.stopPropagation();
-    }
-    iframeScroll = (e: any) => {
-        if (!this._ignoreScroll.includes("outer") && this._outerRef.current) {
-            this._outerRef.current.scrollTop = this.webpage?.scrollTop || 0;
-            this.layoutDoc._scrollTop = this.webpage?.scrollTop;
-        }
-    }
-    onScroll = (e: any) => {
-        if (!this._ignoreScroll.includes("iframe") && this.webpage) {
-            this.webpage.scrollTop = this._outerRef.current?.scrollTop || 0;
-        }
-        if (this._ignoreScroll !== "iframe|outer") {
-            this.layoutDoc._scrollTop = this._outerRef.current?.scrollTop;
-        }
+            if (!LinkDocPreview.LinkInfo && this._outerRef.current) {
+                this.layoutDoc._scrollTop = this._outerRef.current.scrollTop;
+            }
+        }, timeout);
     }
+    iframeWheel = (e: any) => e.stopPropagation();
+    onScroll = (e: any) => this.resetIgnoreScroll();
     scrollFocus = (doc: Doc, smooth: boolean) => {
         let focusSpeed: Opt<number>;
-        if (doc !== this.rootDoc && this.webpage && this._outerRef.current) {
+        if (doc !== this.rootDoc && this.webpage) {
             const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
             if (scrollTo !== undefined) {
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
-                if (!LinkDocPreview.LinkInfo) {
-                    this._ignoreScroll = "iframe|outer";
-                    this.layoutDoc._scrollTop = scrollTo;
-                    this._ignoreScroll = "";
-                }
-                this._ignoreScroll = "iframe|outer";
                 this.goTo(scrollTo, focusSpeed = smooth ? 500 : 0);
-                setTimeout(() => {
-                    this._scrollTimer = undefined;
-                    this._ignoreScroll = "";
-                }, focusSpeed);
             }
         } else {
             this._initialScroll = NumCast(doc.y);
@@ -332,19 +291,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         quickScroll = false;
     }
 
-    _accumulatedGoTo = 0;
-    _resetGoTo: { resetGoTo: { to: number, duration: number } | undefined } = { resetGoTo: undefined };
     goTo = (scrollTop: number, duration: number) => {
-        if (this._outerRef.current && this.webpage) {
+        if (this._outerRef.current) {
             if (duration) {
-                if (this._accumulatedGoTo++) {
-                    this._resetGoTo.resetGoTo = { to: scrollTop, duration };
-                } else {
-                    smoothScroll(duration, [this.webpage as any as HTMLElement, this._outerRef.current], scrollTop, () => this._accumulatedGoTo = 0, this._resetGoTo);
-                }
+                smoothScroll(duration, [this._outerRef.current], scrollTop);
+                this.resetIgnoreScroll(duration);
             } else {
-                this.webpage.scrollTop = scrollTop;
-                this._outerRef.current.scrollTop = scrollTop;
+                this.resetIgnoreScroll();
             }
         }
     }
@@ -354,7 +307,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         document.removeEventListener("pointerup", this.onLongPressUp);
         document.removeEventListener("pointermove", this.onLongPressMove);
         this._iframe?.removeEventListener('wheel', this.iframeWheel);
-        this._iframe?.removeEventListener('scroll', this.iframeScroll);
     }
 
     @action
@@ -672,10 +624,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
     @computed
     get content() {
-        const frozen = !this.props.isSelected() || DocumentDecorations.Instance?.Interacting;
+        const frozen = !this.active() || DocumentDecorations.Instance?.Interacting;
         const scale = this.props.scaling?.() || 1;
         return (<>
-            <div className={"webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
+            <div className={"webBox-cont" + (this.active() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
                 style={{
                     width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / scale}%`,
                     height: `${100 / scale}%`,
@@ -715,12 +667,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             this.props.select(false);
         }
     }
-    @observable isAnnotating = false;
-
-    @action
-    finishMarquee = () => {
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
+    @action finishMarquee = (x?: number, y?: number) => {
         this._marqueeing = undefined;
-        this.isAnnotating = false;
+        this._isAnnotating = false;
+        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false);
     }
 
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
@@ -733,7 +684,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             <div className="webBox" ref={this._mainCont} >
                 <div className={`webBox-container`}
                     style={{ pointerEvents: inactiveLayer ? "none" : undefined }}
-                    onWheel={this.onWebWheel}
                     onContextMenu={this.specificContextMenu}>
                     <base target="_blank" />
                     <div className={"webBox-outerContent"} ref={this._outerRef}
@@ -743,9 +693,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             transform: `scale(${scale})`,
                             pointerEvents: inactiveLayer ? "none" : "all"
                         }}
-                        onWheel={this.onWheel}
-                        onPointerDown={this.onMarqueeDown}
                         onScroll={this.onScroll}
+                        onPointerDown={this.onMarqueeDown}
                     >
                         <div className={"webBox-innerContent"} style={{
                             height: NumCast(this.scrollHeight, 50),
@@ -758,10 +707,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                                 fieldKey={this.annotationKey}
                                 isAnnotationOverlay={true}
                                 scaling={returnOne}
-                                pointerEvents={this.isAnnotating ? "all" : "none"}
+                                pointerEvents={this._isAnnotating ? "all" : "none"}
                                 PanelWidth={this.panelWidth}
                                 PanelHeight={this.panelHeight}
                                 ScreenToLocalTransform={this.scrollXf}
+                                setPreviewCursor={this.setPreviewCursor}
                                 removeDocument={this.removeDocument}
                                 moveDocument={this.moveDocument}
                                 addDocument={this.addDocument}
-- 
cgit v1.2.3-70-g09d2


From f9db6aa1059c6034adb41bc25f932ec5749e6aea Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 15 Mar 2021 14:59:43 -0400
Subject: almost everything working with webBox -- just need to tweak
 scrollHeight

---
 src/client/views/nodes/WebBox.tsx | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 0f10d7cae..a0baf699e 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -37,6 +37,7 @@ import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import { LinkDocPreview } from "./LinkDocPreview";
 import "./WebBox.scss";
 import React = require("react");
+import { LightboxView } from "../LightboxView";
 const htmlToText = require("html-to-text");
 
 type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -63,8 +64,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @observable private _isAnnotating = false;
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
-    get scrollHeight() { return this.webpage?.scrollHeight || 1000; }
-    get webpage() { return this._iframe?.contentDocument?.children[0]; }
+    @computed get scrollHeight() { return 100000; }//this._iframe?.scrollHeight || 1000; }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
 
     constructor(props: any) {
@@ -191,13 +191,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 }
                 if (href) {
                     this.submitURL(href.replace(Utils.prepend(""), Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.origin));
-                    if (this.webpage) {
-                        this.webpage.scrollTop = NumCast(this.layoutDoc._scrollTop);
-                        this.webpage.scrollLeft = 0;
+                    if (this._outerRef.current) {
+                        this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
+                        this._outerRef.current.scrollLeft = 0;
                     }
                 }
             })));
             iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
+            iframe.contentDocument.addEventListener('scroll', () => !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false));
         }
     }
 
@@ -205,7 +206,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         this._scrollTimer && clearTimeout(this._scrollTimer);
         this._scrollTimer = setTimeout(() => {
             this._scrollTimer = undefined;
-            if (!LinkDocPreview.LinkInfo && this._outerRef.current) {
+            if (!LinkDocPreview.LinkInfo && this._outerRef.current &&
+                (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
                 this.layoutDoc._scrollTop = this._outerRef.current.scrollTop;
             }
         }, timeout);
@@ -214,7 +216,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     onScroll = (e: any) => this.resetIgnoreScroll();
     scrollFocus = (doc: Doc, smooth: boolean) => {
         let focusSpeed: Opt<number>;
-        if (doc !== this.rootDoc && this.webpage) {
+        if (doc !== this.rootDoc && this._outerRef.current) {
             const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
             if (scrollTo !== undefined) {
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
-- 
cgit v1.2.3-70-g09d2


From db76556dd1f5c8e6c04a388f5b0f6a46f223efce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 15 Mar 2021 21:41:04 -0400
Subject: final cleanups to get scrolling to work smoothly for webbox's -
 sadly, does not work as well in Firefox, and even worse in Safari.

---
 src/Utils.ts                                       |  38 +++
 src/client/views/MarqueeAnnotator.tsx              |   8 +-
 .../CollectionStackingViewFieldColumn.tsx          |   2 +-
 src/client/views/collections/CollectionSubView.tsx |  21 +-
 src/client/views/nodes/DocumentView.tsx            |   5 +-
 src/client/views/nodes/WebBox.tsx                  | 257 +++++----------------
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 7 files changed, 118 insertions(+), 215 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 936a459ba..d2713762d 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -598,6 +598,44 @@ export function lightOrDark(color: any) {
     }
 }
 
+
+export function getWordAtPoint(elem: any, x: number, y: number): Opt<string> {
+    if (elem.nodeType == elem.TEXT_NODE) {
+        var range = elem.ownerDocument.createRange();
+        range.selectNodeContents(elem);
+        var currentPos = 0;
+        var endPos = range.endOffset;
+        while (currentPos + 1 < endPos) {
+            range.setStart(elem, currentPos);
+            range.setEnd(elem, currentPos + 1);
+            const rangeRect = range.getBoundingClientRect();
+            if (rangeRect.left <= x && rangeRect.right >= x &&
+                rangeRect.top <= y && rangeRect.bottom >= y) {
+                range.expand("word");
+                var ret = range.toString();
+                range.detach();
+                return (ret);
+            }
+            currentPos += 1;
+        }
+    } else {
+        for (var i = 0; i < elem.childNodes.length; i++) {
+            var range = elem.childNodes[i].ownerDocument.createRange();
+            range.selectNodeContents(elem.childNodes[i]);
+            const rangeRect = range.getBoundingClientRect();
+            if (rangeRect.left <= x && rangeRect.right >= x &&
+                rangeRect.top <= y && rangeRect.bottom >= y) {
+                range.detach();
+                const word = getWordAtPoint(elem.childNodes[i], x, y);
+                if (word) return word;
+            } else {
+                range.detach();
+            }
+        }
+    }
+    return undefined;
+}
+
 export function hasDescendantTarget(x: number, y: number, target: HTMLDivElement | null) {
     let entered = false;
     for (let child = document.elementFromPoint(x, y); !entered && child; child = child.parentElement) {
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 59500070c..77ace7ddb 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -59,8 +59,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         this._startX = this._left = (this.props.down[0] - boundingRect.left) * (this.props.mainCont.offsetWidth / boundingRect.width);
         this._startY = this._top = (this.props.down[1] - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height) + this.props.mainCont.scrollTop;
         this._height = this._width = 0;
-        document.addEventListener("pointermove", this.onSelectMove);
-        document.addEventListener("pointerup", this.onSelectEnd);
+        document.addEventListener("pointermove", this.onSelectMove, true);
+        document.addEventListener("pointerup", this.onSelectEnd, true);
 
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.(this.highlight("rgba(173, 216, 230, 0.75)", true));
         AnchorMenu.Instance.Highlight = this.highlight;
@@ -91,8 +91,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         });
     }
     componentWillUnmount() {
-        document.removeEventListener("pointermove", this.onSelectMove);
-        document.removeEventListener("pointerup", this.onSelectEnd);
+        document.removeEventListener("pointermove", this.onSelectMove, true);
+        document.removeEventListener("pointerup", this.onSelectEnd, true);
     }
 
     @undoBatch
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 94d74b6f7..75ec6cd1a 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -146,7 +146,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         newDoc.heading = heading;
         FormattedTextBox.SelectOnLoad = newDoc[Id];
         FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? "" : " ";
-        return this.props.parent.props.addDocument?.(newDoc) || false;
+        return this.props.parent.addDocument?.(newDoc) || false;
     }
 
     @action
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3bde55af9..8128c18d7 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -373,16 +373,17 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             }
             if (uriList) {
                 console.log("Web URI = ", uriList);
-                const existingWebDoc = await Hypothesis.findWebDoc(uriList);
-                if (existingWebDoc) {
-                    const alias = Doc.MakeAlias(existingWebDoc);
-                    alias.x = options.x;
-                    alias.y = options.y;
-                    alias._nativeWidth = 850;
-                    alias._height = 512;
-                    alias._width = 400;
-                    addDocument(alias);
-                } else {
+                // const existingWebDoc = await Hypothesis.findWebDoc(uriList);
+                // if (existingWebDoc) {
+                //     const alias = Doc.MakeAlias(existingWebDoc);
+                //     alias.x = options.x;
+                //     alias.y = options.y;
+                //     alias._nativeWidth = 850;
+                //     alias._height = 512;
+                //     alias._width = 400;
+                //     addDocument(alias);
+                // } else 
+                {
                     console.log("Adding ...");
                     const newDoc = Docs.Create.WebDocument(uriList.split("#annotations:")[0], {// clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
                         ...options,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8dea34a15..aec6cd03d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -479,7 +479,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {  // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
                 } else {
-                    this.props.select(e.ctrlKey || e.shiftKey);
+                    const ctrlPressed = e.ctrlKey || e.shiftKey;
+                    if (this.props.Document.type === DocumentType.WEB) {
+                        this._timeout = setTimeout(() => { this._timeout = undefined; this.props.select(ctrlPressed); }, 350);
+                    } else this.props.select(ctrlPressed);
                 }
                 preventDefault = false;
             }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a0baf699e..764dcd7a3 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -13,12 +13,10 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { WebField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, returnOne, returnTrue, returnZero, smoothScroll, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, getWordAtPoint, returnOne, returnTrue, returnZero, smoothScroll, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DragManager } from "../../util/DragManager";
-import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CollectionStackingView } from "../collections/CollectionStackingView";
@@ -38,6 +36,7 @@ import { LinkDocPreview } from "./LinkDocPreview";
 import "./WebBox.scss";
 import React = require("react");
 import { LightboxView } from "../LightboxView";
+import { SnappingManager } from "../../util/SnappingManager";
 const htmlToText = require("html-to-text");
 
 type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -49,22 +48,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _disposers: { [name: string]: IReactionDisposer } = {};
-    private _longPressSecondsHack?: NodeJS.Timeout;
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
-    private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
-    private _iframeDragRef = React.createRef<HTMLDivElement>();
     private _keyInput = React.createRef<HTMLInputElement>();
-    private _scrollTimer: any;
+    @observable _scrollTimer: any;
+    @observable private _overlayAnnoInfo: Opt<Doc>;
     private _initialScroll: Opt<number>;
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
     @observable private _marqueeing: number[] | undefined;
     @observable private _url: string = "hello";
-    @observable private _pressX: number = 0;
-    @observable private _pressY: number = 0;
     @observable private _isAnnotating = false;
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
-    @computed get scrollHeight() { return 100000; }//this._iframe?.scrollHeight || 1000; }
+    @observable private _scrollHeight = 1500;
+    @computed get scrollHeight() { return this._scrollHeight; }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
 
     constructor(props: any) {
@@ -108,9 +104,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     }
 
     @action
-    ptrUp = (e: PointerEvent) => {
+    iframeUp = (e: PointerEvent) => {
         if (this._iframe?.contentWindow && this._iframe.contentDocument && !this._iframe.contentWindow.getSelection()?.isCollapsed) {
-            this._iframe.contentDocument.addEventListener("pointerup", this.ptrUp);
+            this._iframe.contentDocument.addEventListener("pointerup", this.iframeUp);
             const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
             const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
@@ -121,51 +117,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             }
         } else AnchorMenu.Instance.fadeOut(true);
     }
-    getWordAtPoint = (elem: any, x: number, y: number): Opt<string> => {
-        if (elem.nodeType == elem.TEXT_NODE) {
-            var range = elem.ownerDocument.createRange();
-            range.selectNodeContents(elem);
-            var currentPos = 0;
-            var endPos = range.endOffset;
-            while (currentPos + 1 < endPos) {
-                range.setStart(elem, currentPos);
-                range.setEnd(elem, currentPos + 1);
-                const rangeRect = range.getBoundingClientRect();
-                if (rangeRect.left <= x && rangeRect.right >= x &&
-                    rangeRect.top <= y && rangeRect.bottom >= y) {
-                    range.expand("word");
-                    var ret = range.toString();
-                    range.detach();
-                    return (ret);
-                }
-                currentPos += 1;
-            }
-        } else {
-            for (var i = 0; i < elem.childNodes.length; i++) {
-                var range = elem.childNodes[i].ownerDocument.createRange();
-                range.selectNodeContents(elem.childNodes[i]);
-                const rangeRect = range.getBoundingClientRect();
-                if (rangeRect.left <= x && rangeRect.right >= x &&
-                    rangeRect.top <= y && rangeRect.bottom >= y) {
-                    range.detach();
-                    const word = this.getWordAtPoint(elem.childNodes[i], x, y);
-                    if (word) return word;
-                } else {
-                    range.detach();
-                }
-            }
-        }
-        return undefined;
-    }
     @action
-    ptrDown = (e: PointerEvent) => {
+    iframeDown = (e: PointerEvent) => {
         const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
         const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
-        const word = this.getWordAtPoint(e.target, e.clientX, e.clientY);
+        const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
         e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
         if (word) {
-            this._iframe?.contentDocument?.addEventListener("pointerup", this.ptrUp);
+            this._iframe?.contentDocument?.addEventListener("pointerup", this.iframeUp);
             setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
         } else {
             this._isAnnotating = true;
@@ -175,10 +135,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
+    @action
     iframeLoaded = (e: any) => {
         const iframe = this._iframe;
         if (iframe?.contentDocument) {
-            iframe?.contentDocument.addEventListener("pointerdown", this.ptrDown);
+            iframe?.contentDocument.addEventListener("pointerdown", this.iframeDown);
+            this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight);
             if (this._initialScroll !== undefined && this._outerRef.current) {
                 this._outerRef.current.scrollTop = this._initialScroll;
                 this._initialScroll = undefined;
@@ -202,31 +164,44 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         }
     }
 
-    resetIgnoreScroll = (timeout: number = 250) => {
+    @action
+    setDashScrollTop = (scrollTop: number, timeout: number = 250) => {
+        const iframeHeight = this._scrollHeight - this.panelHeight();
+        timeout = scrollTop > iframeHeight ? 0 : timeout;
         this._scrollTimer && clearTimeout(this._scrollTimer);
-        this._scrollTimer = setTimeout(() => {
+        this._scrollTimer = setTimeout(action(() => {
             this._scrollTimer = undefined;
             if (!LinkDocPreview.LinkInfo && this._outerRef.current &&
                 (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
-                this.layoutDoc._scrollTop = this._outerRef.current.scrollTop;
+                if (scrollTop > iframeHeight)
+                    this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = iframeHeight;
+                else this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = scrollTop;
             }
-        }, timeout);
+        }), timeout);
     }
-    iframeWheel = (e: any) => e.stopPropagation();
-    onScroll = (e: any) => this.resetIgnoreScroll();
+    @action
+    iframeWheel = (e: any) => {
+        if (!this._scrollTimer) {
+            this._scrollTimer = setTimeout(action(() => this._scrollTimer = undefined), 250); // this turns events off on the iframe which allows scrolling to change direction smoothly
+        }
+    }
+    onWheel = (e: any) => {
+        e.stopPropagation();
+        e.preventDefault();
+    }
+    onScroll = (e: any) => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0);
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        let focusSpeed: Opt<number>;
         if (doc !== this.rootDoc && this._outerRef.current) {
             const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
             if (scrollTo !== undefined) {
+                let focusSpeed = smooth ? 500 : 0;
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
-                this.goTo(scrollTo, focusSpeed = smooth ? 500 : 0);
+                this.goTo(scrollTo, focusSpeed);
+                return focusSpeed;
             }
-        } else {
-            this._initialScroll = NumCast(doc.y);
         }
-
-        return focusSpeed;
+        this._initialScroll = NumCast(doc.y);
+        return 0;
     }
 
     getAnchor = () => {
@@ -253,8 +228,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 this._savedAnnotations.clear();
             }));
 
-        document.addEventListener("pointerup", this.onLongPressUp);
-        document.addEventListener("pointermove", this.onLongPressMove);
         const field = Cast(this.rootDoc[this.props.fieldKey], WebField);
         if (field?.url.href.indexOf("youtube") !== -1) {
             const youtubeaspect = 400 / 315;
@@ -295,20 +268,20 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
     goTo = (scrollTop: number, duration: number) => {
         if (this._outerRef.current) {
+            const iframeHeight = this._scrollHeight - this.panelHeight();
+            scrollTop = scrollTop > iframeHeight + 50 ? iframeHeight : scrollTop;
             if (duration) {
                 smoothScroll(duration, [this._outerRef.current], scrollTop);
-                this.resetIgnoreScroll(duration);
+                this.setDashScrollTop(scrollTop, duration);
             } else {
-                this.resetIgnoreScroll();
+                this.setDashScrollTop(scrollTop);
             }
         }
     }
 
     componentWillUnmount() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
-        document.removeEventListener("pointerup", this.onLongPressUp);
-        document.removeEventListener("pointermove", this.onLongPressMove);
-        this._iframe?.removeEventListener('wheel', this.iframeWheel);
+        this._iframe?.removeEventListener('wheel', this.iframeWheel, true);
     }
 
     @action
@@ -413,104 +386,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         );
     }
 
-    _ignore = 0;
-    onPreWheel = (e: React.WheelEvent) => this._ignore = e.timeStamp;
-    onPrePointer = (e: React.PointerEvent) => this._ignore = e.timeStamp;
-    onPostPointer = (e: React.PointerEvent) => this._ignore !== e.timeStamp && e.stopPropagation();
-    onPostWheel = (e: React.WheelEvent) => this._ignore !== e.timeStamp && e.stopPropagation();
-
-    onLongPressDown = (e: React.PointerEvent) => {
-        this._pressX = e.clientX;
-        this._pressY = e.clientY;
-
-        // find the pressed element in the iframe (currently only works if its an img)
-        let pressedElement: HTMLElement | undefined;
-        let pressedBound: ClientRect | undefined;
-        let selectedText: string = "";
-        let pressedImg: boolean = false;
-        if (this._iframe) {
-            const B = this._iframe.getBoundingClientRect();
-            const iframeDoc = this._iframe.contentDocument;
-            if (B && iframeDoc) {
-                // TODO: this only works when scale = 1 as it is currently only inteded for mobile upload
-                const element = iframeDoc.elementFromPoint(this._pressX - B.left, this._pressY - B.top);
-                if (element && element.nodeName === "IMG") {
-                    pressedBound = element.getBoundingClientRect();
-                    pressedElement = element.cloneNode(true) as HTMLElement;
-                    pressedImg = true;
-                } else {
-                    // check if there is selected text
-                    const text = iframeDoc.getSelection();
-                    if (text && text.toString().length > 0) {
-                        selectedText = text.toString();
-
-                        // get html of the selected text
-                        const range = text.getRangeAt(0);
-                        const contents = range.cloneContents();
-                        const div = document.createElement("div");
-                        div.appendChild(contents);
-                        pressedElement = div;
-
-                        pressedBound = range.getBoundingClientRect();
-                    }
-                }
-            }
-        }
-
-        // mark the pressed element
-        if (pressedElement && pressedBound) {
-            if (this._iframeIndicatorRef.current) {
-                this._iframeIndicatorRef.current.style.top = pressedBound.top + "px";
-                this._iframeIndicatorRef.current.style.left = pressedBound.left + "px";
-                this._iframeIndicatorRef.current.style.width = pressedBound.width + "px";
-                this._iframeIndicatorRef.current.style.height = pressedBound.height + "px";
-                this._iframeIndicatorRef.current.classList.add("active");
-            }
-        }
-
-        // start dragging the pressed element if long pressed
-        this._longPressSecondsHack = setTimeout(() => {
-            if (pressedImg && pressedElement && pressedBound) {
-                e.stopPropagation();
-                e.preventDefault();
-                if (pressedElement.nodeName === "IMG") {
-                    const src = pressedElement.getAttribute("src"); // TODO: may not always work
-                    if (src) {
-                        const doc = Docs.Create.ImageDocument(src);
-                        ImageUtils.ExtractExif(doc);
-
-                        // add clone to div so that dragging ghost is placed properly
-                        if (this._iframeDragRef.current) this._iframeDragRef.current.appendChild(pressedElement);
-
-                        const dragData = new DragManager.DocumentDragData([doc]);
-                        DragManager.StartDocumentDrag([pressedElement], dragData, this._pressX, this._pressY, { hideSource: true });
-                    }
-                }
-            } else if (selectedText && pressedBound && pressedElement) {
-                e.stopPropagation();
-                e.preventDefault();
-                // create doc with the selected text's html
-                const doc = Docs.Create.HtmlDocument(pressedElement.innerHTML);
-
-                // create dragging ghost with the selected text
-                if (this._iframeDragRef.current) this._iframeDragRef.current.appendChild(pressedElement);
-
-                // start the drag
-                const dragData = new DragManager.DocumentDragData([doc]);
-                DragManager.StartDocumentDrag([pressedElement], dragData, this._pressX - pressedBound.top, this._pressY - pressedBound.top, { hideSource: true });
-            }
-        }, 1500);
-    }
-    onLongPressMove = (e: PointerEvent) => {
-        // this._pressX = e.clientX;
-        // this._pressY = e.clientY;
-    }
-    onLongPressUp = (e: PointerEvent) => {
-        this._longPressSecondsHack && clearTimeout(this._longPressSecondsHack);
-        this._iframeIndicatorRef.current?.classList.remove("active");
-        while (this._iframeDragRef.current?.firstChild) this._iframeDragRef.current.removeChild(this._iframeDragRef.current.firstChild);
-    }
-
     specificContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
@@ -529,12 +404,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         } else if (field instanceof WebField) {
             const url = this.layoutDoc.useCors ? Utils.CorsProxy(field.url.href) : field.url.href;
             //    view = <iframe className="webBox-iframe" src={url} onLoad={e => { e.currentTarget.before((e.currentTarget.contentDocument?.body || e.currentTarget.contentDocument)?.children[0]!); e.currentTarget.remove(); }}
-            view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded}
+            view = <iframe className="webBox-iframe" enable-annotation={"true"}
+                style={{ pointerEvents: this._scrollTimer ? "none" : undefined }}
+                ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded}
                 // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
                 // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
                 sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin"} />;
         } else {
-            view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
+            view = <iframe className="webBox-iframe" enable-annotation={"true"}
+                style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
+                ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
         }
         return view;
     }
@@ -626,33 +505,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
     @computed
     get content() {
-        const frozen = !this.active() || DocumentDecorations.Instance?.Interacting;
-        const scale = this.props.scaling?.() || 1;
-        return (<>
-            <div className={"webBox-cont" + (this.active() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
-                style={{
-                    width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / scale}%`,
-                    height: `${100 / scale}%`,
-                }}
-                onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
-                {this.urlContent}
-            </div>
-            {!frozen ? (null) :
-                <div className="webBox-overlay" style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? undefined : "all" }}
-                    onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
-                    <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
-                        <div className="indicator" ref={this._iframeIndicatorRef}></div>
-                        <div className="dragger" ref={this._iframeDragRef}></div>
-                    </div>
-                </div>}
-        </>);
+        return <div className={"webBox-cont" + (this.active() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
+            style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / (this.props.scaling?.() || 1)}%`, }}>
+            {this.urlContent}
+        </div>;
     }
 
     showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    @observable private _overlayAnnoInfo: Opt<Doc>;
-    @computed get allAnnotations() {
-        return DocListCast(this.dataDoc[this.annotationKey]);
-    }
+    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get annotationLayer() {
         TraceMobx();
         return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
@@ -683,7 +543,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false;
         const scale = this.props.scaling?.() || 1;
         return (
-            <div className="webBox" ref={this._mainCont} >
+            <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.active() || SnappingManager.GetIsDragging() ? undefined : "none" }} >
                 <div className={`webBox-container`}
                     style={{ pointerEvents: inactiveLayer ? "none" : undefined }}
                     onContextMenu={this.specificContextMenu}>
@@ -693,8 +553,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             width: `calc(${100 / scale}% - ${this.sidebarWidth()}px)`,
                             height: `${100 / scale}%`,
                             transform: `scale(${scale})`,
-                            pointerEvents: inactiveLayer ? "none" : "all"
+                            pointerEvents: inactiveLayer ? "none" : undefined
                         }}
+                        onWheel={this.onWheel}
                         onScroll={this.onScroll}
                         onPointerDown={this.onMarqueeDown}
                     >
@@ -709,7 +570,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                                 fieldKey={this.annotationKey}
                                 isAnnotationOverlay={true}
                                 scaling={returnOne}
-                                pointerEvents={this._isAnnotating ? "all" : "none"}
+                                pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"}
                                 PanelWidth={this.panelWidth}
                                 PanelHeight={this.panelHeight}
                                 ScreenToLocalTransform={this.scrollXf}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c9bee5101..e34bc8ffa 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -400,7 +400,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     }
 
     @action
-    finishMarquee = () => {
+    finishMarquee = (x?: number, y?: number) => {
         this._marqueeing = undefined;
         this._textSelecting = true;
         document.removeEventListener("pointermove", this.onSelectMove);
-- 
cgit v1.2.3-70-g09d2


From 73dec0f973be7007093fcfd145f166d47a35cd97 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 15 Mar 2021 22:38:21 -0400
Subject: from last

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

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 764dcd7a3..19a5945e2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -550,7 +550,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                     <base target="_blank" />
                     <div className={"webBox-outerContent"} ref={this._outerRef}
                         style={{
-                            width: `calc(${100 / scale}% - ${this.sidebarWidth()}px)`,
+                            width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale}px)`,
                             height: `${100 / scale}%`,
                             transform: `scale(${scale})`,
                             pointerEvents: inactiveLayer ? "none" : undefined
-- 
cgit v1.2.3-70-g09d2


From ecaf1da499b41d0f873c64cb128118a6a291229e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 15 Mar 2021 23:42:19 -0400
Subject: fixed warnings.  made savedAnnotations an ObservableMap. turned off
 annotationLayer when there are no annotations - text is blurry on windows
 Chrome with mix-blend-mode overlay

---
 src/Utils.ts                                       | 22 ++++++------
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/DragManager.ts                     |  4 +--
 src/client/views/LightboxView.tsx                  |  7 ++--
 src/client/views/MarqueeAnnotator.tsx              | 41 +++++++++++-----------
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 src/client/views/nodes/ImageBox.tsx                | 18 +++++++---
 src/client/views/nodes/WebBox.tsx                  | 17 +++++----
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 | 11 +++---
 10 files changed, 70 insertions(+), 56 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index d2713762d..dab3548b3 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -484,12 +484,12 @@ const easeInOutQuad = (currentTime: number, start: number, change: number, durat
 
 export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number) {
     const elements = (element instanceof HTMLElement ? [element] : element);
-    let starts = elements.map(element => element.scrollTop);
-    let startDate = new Date().getTime();
+    const starts = elements.map(element => element.scrollTop);
+    const startDate = new Date().getTime();
 
     const animateScroll = () => {
         const currentDate = new Date().getTime();
-        let currentTime = currentDate - startDate;
+        const currentTime = currentDate - startDate;
         elements.map((element, i) => element.scrollTop = easeInOutQuad(currentTime, starts[i], to - starts[i], duration));
 
         if (currentTime < duration) {
@@ -599,12 +599,12 @@ export function lightOrDark(color: any) {
 }
 
 
-export function getWordAtPoint(elem: any, x: number, y: number): Opt<string> {
-    if (elem.nodeType == elem.TEXT_NODE) {
+export function getWordAtPoint(elem: any, x: number, y: number): string | undefined {
+    if (elem.nodeType === elem.TEXT_NODE) {
         var range = elem.ownerDocument.createRange();
         range.selectNodeContents(elem);
         var currentPos = 0;
-        var endPos = range.endOffset;
+        const endPos = range.endOffset;
         while (currentPos + 1 < endPos) {
             range.setStart(elem, currentPos);
             range.setEnd(elem, currentPos + 1);
@@ -612,21 +612,21 @@ export function getWordAtPoint(elem: any, x: number, y: number): Opt<string> {
             if (rangeRect.left <= x && rangeRect.right >= x &&
                 rangeRect.top <= y && rangeRect.bottom >= y) {
                 range.expand("word");
-                var ret = range.toString();
+                const ret = range.toString();
                 range.detach();
                 return (ret);
             }
             currentPos += 1;
         }
     } else {
-        for (var i = 0; i < elem.childNodes.length; i++) {
-            var range = elem.childNodes[i].ownerDocument.createRange();
-            range.selectNodeContents(elem.childNodes[i]);
+        for (let childNode of elem.childNodes) {
+            const range = childNode.ownerDocument.createRange();
+            range.selectNodeContents(childNode);
             const rangeRect = range.getBoundingClientRect();
             if (rangeRect.left <= x && rangeRect.right >= x &&
                 rangeRect.top <= y && rangeRect.bottom >= y) {
                 range.detach();
-                const word = getWordAtPoint(elem.childNodes[i], x, y);
+                const word = getWordAtPoint(childNode, x, y);
                 if (word) return word;
             } else {
                 range.detach();
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8059509ab..6e892cb6e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -324,7 +324,7 @@ export namespace Docs {
             }],
             [DocumentType.WEB, {
                 layout: { view: WebBox, dataField: defaultDataKey },
-                options: { _height: 300, scrollHeight: 100000, _fitWidth: true }
+                options: { _height: 300, _fitWidth: true }
             }],
             [DocumentType.COL, {
                 layout: { view: CollectionView, dataField: defaultDataKey },
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0d154bc3a..dc95193ea 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -157,8 +157,8 @@ export namespace DragManager {
     // used by PDFs,Text,Image,Video,Web to conditionally (if the drop completes) create a text annotation when dragging the annotate button from the AnchorMenu when a text/region selection has been made.
     // this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
     export class AnchorAnnoDragData extends LinkDragData {
-        constructor(dragDoc: Doc, linkSourceGetAnchor: () => Doc, dropDocCreator: (annotationOn: Doc | undefined) => Doc) {
-            super(dragDoc, linkSourceGetAnchor);
+        constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc, dropDocCreator: (annotationOn: Doc | undefined) => Doc) {
+            super(dragView, linkSourceGetAnchor);
             this.dropDocCreator = dropDocCreator;
             this.offset = [0, 0];
         }
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 9b14c180e..731d46502 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -30,15 +30,16 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     @observable private static _docTarget: Opt<Doc>;
     @observable private static _docFilters: string[] = []; // filters
     @observable private static _tourMap: Opt<Doc[]> = [];   // list of all tours available from the current target
-    private static _savedState: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number> }>;
+    private static _savedState: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }>;
     private static _history: Opt<{ doc: Doc, target?: Doc }[]> = [];
     private static _future: Opt<Doc[]> = [];
     private static _docView: Opt<DocumentView>;
-    static path: { doc: Opt<Doc>, target: Opt<Doc>, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt<Doc[]>, saved: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number> }> }[] = [];
+    static path: { doc: Opt<Doc>, target: Opt<Doc>, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt<Doc[]>, saved: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }> }[] = [];
     @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[]) {
         if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
             this.LightboxDoc._panX = this._savedState.panX;
             this.LightboxDoc._panY = this._savedState.panY;
+            this.LightboxDoc._scrollTop = this._savedState.scrollTop;
             this.LightboxDoc._viewScale = this._savedState.scale;
             this.LightboxDoc._viewTransition = undefined;
         }
@@ -53,6 +54,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     panX: Cast(doc._panX, "number", null),
                     panY: Cast(doc._panY, "number", null),
                     scale: Cast(doc._viewScale, "number", null),
+                    scrollTop: Cast(doc._scrollTop, "number", null),
                 };
             }
         }
@@ -123,6 +125,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     LightboxView.LightboxDoc._panX = saved.panX;
                     LightboxView.LightboxDoc._panY = saved.panY;
                     LightboxView.LightboxDoc._viewScale = saved.scale;
+                    LightboxView.LightboxDoc._scrollTop = saved.scrollTop;
                     LightboxView.LightboxDoc._viewTransition = undefined;
                 }
                 const pop = LightboxView.path.pop();
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 77ace7ddb..1e97f9b41 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,22 +1,22 @@
-import { action, observable, runInAction } from "mobx";
+import { action, observable, runInAction, ObservableMap } from "mobx";
 import { observer } from "mobx-react";
 import { Dictionary } from "typescript-collections";
 import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
+import { List } from "../../fields/List";
+import { NumCast } from "../../fields/Types";
 import { GetEffectiveAcl } from "../../fields/util";
-import { DocUtils, Docs } from "../documents/Documents";
+import { Utils } from "../../Utils";
+import { Docs } from "../documents/Documents";
+import { DocumentType } from "../documents/DocumentTypes";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { DragManager } from "../util/DragManager";
+import { undoBatch } from "../util/UndoManager";
+import "./MarqueeAnnotator.scss";
+import { DocumentView } from "./nodes/DocumentView";
 import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
 import { AnchorMenu } from "./pdf/AnchorMenu";
-import "./MarqueeAnnotator.scss";
 import React = require("react");
-import { undoBatch } from "../util/UndoManager";
-import { NumCast } from "../../fields/Types";
-import { DocumentType } from "../documents/DocumentTypes";
-import { List } from "../../fields/List";
-import { Utils } from "../../Utils";
-import { util } from "chai";
 const _global = (window /* browser */ || global /* node */) as any;
 
 export interface MarqueeAnnotatorProps {
@@ -26,7 +26,8 @@ export interface MarqueeAnnotatorProps {
     scaling?: () => number;
     containerOffset?: () => number[];
     mainCont: HTMLDivElement;
-    savedAnnotations: Dictionary<number, HTMLDivElement[]>;
+    docView: DocumentView;
+    savedAnnotations: ObservableMap<number, HTMLDivElement[]>;
     annotationLayer: HTMLDivElement;
     addDocument: (doc: Doc) => boolean;
     getPageFromScroll?: (top: number) => number;
@@ -48,7 +49,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             AnchorMenu.Instance.Status = "marquee";
             AnchorMenu.Instance.fadeOut(true);
             // clear out old marquees and initialize menu for new selection
-            this.props.savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+            Array.from(this.props.savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
             this.props.savedAnnotations.clear();
         });
     }
@@ -81,7 +82,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                 FormattedTextBox.SelectOnLoad = target[Id];
                 return target;
             };
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.rootDoc, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                 dragComplete: e => {
                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
                         e.annoDragData.linkSourceDoc.isPushpin = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
@@ -98,10 +99,10 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     @undoBatch
     @action
     makeAnnotationDocument = (color: string): Opt<Doc> => {
-        if (this.props.savedAnnotations.size() === 0) return undefined;
-        if ((this.props.savedAnnotations.values()[0][0] as any).marqueeing) {
+        if (this.props.savedAnnotations.size === 0) return undefined;
+        if ((Array.from(this.props.savedAnnotations.values())[0][0] as any).marqueeing) {
             const scale = this.props.scaling?.() || 1;
-            const anno = this.props.savedAnnotations.values()[0][0];
+            const anno = Array.from(this.props.savedAnnotations.values())[0][0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
             const marqueeAnno = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title });
             marqueeAnno.x = (parseInt(anno.style.left || "0") - containerOffset[0]) / scale;
@@ -117,7 +118,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
         const annoDocs: Doc[] = [];
-        this.props.savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => {
+        this.props.savedAnnotations.forEach((value: HTMLDivElement[], key: number) => value.map(anno => {
             const textRegion = new Doc();
             textRegion.x = parseInt(anno.style.left ?? "0");
             textRegion.y = parseInt(anno.style.top ?? "0");
@@ -149,20 +150,20 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         return annotationDoc as Doc ?? undefined;
     }
 
-    public static previewNewAnnotation = action((savedAnnotations: Dictionary<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
+    public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
         if (div.style.top) {
             div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString();
         }
         annotationLayer.append(div);
         div.style.backgroundColor = "#ACCEF7";
         div.style.opacity = "0.5";
-        const savedPage = savedAnnotations.getValue(page);
+        const savedPage = savedAnnotations.get(page);
         if (savedPage) {
             savedPage.push(div);
-            savedAnnotations.setValue(page, savedPage);
+            savedAnnotations.set(page, savedPage);
         }
         else {
-            savedAnnotations.setValue(page, [div]);
+            savedAnnotations.set(page, [div]);
         }
     });
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3556e74bc..b9757dde3 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -381,7 +381,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             const otherSet = new Set<Doc>();
             otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
             tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc));
-            const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d as Doc).filter(d => d.type !== DocumentType.KVP);
+            const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP);
             other && (Doc.GetProto(other).data = new List<Doc>(vals));
         }, 0);
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index bc01acdfd..0be0b5f30 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap } from 'mobx';
 import { observer } from "mobx-react";
 import { Dictionary } from 'typescript-collections';
 import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc';
@@ -63,7 +63,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
     componentDidMount() {
         this._disposers.selection = reaction(() => this.props.isSelected(),
             selected => !selected && setTimeout(() => {
-                this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
                 this._savedAnnotations.clear();
             }));
         this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
@@ -337,7 +337,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     @observable _marqueeing: number[] | undefined;
-    @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get annotationLayer() {
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
@@ -388,7 +388,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
             </CollectionFreeFormView>
             {this.annotationLayer}
             {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                <MarqueeAnnotator rootDoc={this.rootDoc} scrollTop={0} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
+                <MarqueeAnnotator rootDoc={this.rootDoc}
+                    scrollTop={0} down={this._marqueeing}
+                    scaling={this.props.scaling}
+                    docView={this.props.docViewPath().lastElement()}
+                    addDocument={this.addDocument}
+                    finishMarquee={this.finishMarquee}
+                    savedAnnotations={this._savedAnnotations}
+                    annotationLayer={this._annotationLayer.current}
+                    mainCont={this._mainCont.current}
+                />}
         </div >);
     }
+
 }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 19a5945e2..bc20c3b47 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap } from "mobx";
 import { observer } from "mobx-react";
 import { Dictionary } from "typescript-collections";
 import * as WebRequest from 'web-request';
@@ -58,7 +58,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @observable private _url: string = "hello";
     @observable private _isAnnotating = false;
     @observable private _iframe: HTMLIFrameElement | null = null;
-    @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _scrollHeight = 1500;
     @computed get scrollHeight() { return this._scrollHeight; }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
@@ -111,7 +111,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
-                this.createTextAnnotation(sel, sel.getRangeAt(0))
+                this.createTextAnnotation(sel, sel.getRangeAt(0));
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX,
                     e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
             }
@@ -173,9 +173,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             this._scrollTimer = undefined;
             if (!LinkDocPreview.LinkInfo && this._outerRef.current &&
                 (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
-                if (scrollTop > iframeHeight)
-                    this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = iframeHeight;
-                else this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = scrollTop;
+                this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
             }
         }), timeout);
     }
@@ -194,7 +192,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         if (doc !== this.rootDoc && this._outerRef.current) {
             const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
             if (scrollTo !== undefined) {
-                let focusSpeed = smooth ? 500 : 0;
+                const focusSpeed = smooth ? 500 : 0;
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
                 this.goTo(scrollTo, focusSpeed);
                 return focusSpeed;
@@ -224,7 +222,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
         this._disposers.selection = reaction(() => this.props.isSelected(),
             selected => !selected && setTimeout(() => {
-                this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
                 this._savedAnnotations.clear();
             }));
 
@@ -515,7 +513,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+        return <div className="webBox-annotationLayer" style={{ display: !this._savedAnnotations.size && !this.allAnnotations.length ? "none" : undefined, height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
             {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
                 <Annotation {...this.props} fieldKey={this.annotationKey} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)
             }
@@ -590,6 +588,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                             scrollTop={0}
                             down={this._marqueeing} scaling={returnOne}
                             addDocument={this.addDocument}
+                            docView={this.props.docViewPath().lastElement()}
                             finishMarquee={this.finishMarquee}
                             savedAnnotations={this._savedAnnotations}
                             annotationLayer={this._annotationLayer.current}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9259e6c25..af34a53ec 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -229,7 +229,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 return target;
             };
 
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.rootDoc, this.getAnchor, targetCreator), e.pageX, e.pageY);
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), this.getAnchor, targetCreator), e.pageX, e.pageY);
         });
         const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
         this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index e34bc8ffa..91bb321b2 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 } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap } from "mobx";
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
@@ -65,7 +65,7 @@ interface IViewerProps extends FieldViewProps {
 export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocument>(PdfDocument) {
     static _annotationStyle: any = addStyleSheet();
     @observable private _pageSizes: { width: number, height: number }[] = [];
-    @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript;
     @observable private _marqueeing: number[] | undefined;
     @observable private _textSelecting = true;
@@ -140,8 +140,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         this._disposers.selected = reaction(() => this.props.isSelected(),
             selected => {
                 if (!selected) {
-                    this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
-                    this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, []));
+                    Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
+                    Array.from(this._savedAnnotations.keys()).forEach(k => this._savedAnnotations.set(k, []));
                 }
                 (SelectionManager.Views().length === 1) && this.setupPdfJsViewer();
             },
@@ -390,7 +390,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
                 // clear out old marquees and initialize menu for new selection
                 AnchorMenu.Instance.Status = "marquee";
-                this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
                 this._savedAnnotations.clear();
                 this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" });
                 document.addEventListener("pointerup", this.onSelectEnd);
@@ -574,6 +574,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                         anchorMenuClick={this.props.anchorMenuClick}
                         addDocument={this.addDocument}
                         finishMarquee={this.finishMarquee}
+                        docView={this.props.docViewPath().lastElement()}
                         getPageFromScroll={this.getPageFromScroll}
                         savedAnnotations={this._savedAnnotations}
                         annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
-- 
cgit v1.2.3-70-g09d2


From c27d8d20b44ba2fb8b0ddb2ef077ce155df7fa84 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 16 Mar 2021 00:19:55 -0400
Subject: fixed warnings

---
 src/Utils.ts                        | 6 +++---
 src/client/views/nodes/PDFBox.tsx   | 2 +-
 src/client/views/nodes/VideoBox.tsx | 7 ++++---
 src/client/views/nodes/WebBox.tsx   | 2 +-
 4 files changed, 9 insertions(+), 8 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index dab3548b3..ef9c51b8b 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -601,7 +601,7 @@ export function lightOrDark(color: any) {
 
 export function getWordAtPoint(elem: any, x: number, y: number): string | undefined {
     if (elem.nodeType === elem.TEXT_NODE) {
-        var range = elem.ownerDocument.createRange();
+        const range = elem.ownerDocument.createRange();
         range.selectNodeContents(elem);
         var currentPos = 0;
         const endPos = range.endOffset;
@@ -611,7 +611,7 @@ export function getWordAtPoint(elem: any, x: number, y: number): string | undefi
             const rangeRect = range.getBoundingClientRect();
             if (rangeRect.left <= x && rangeRect.right >= x &&
                 rangeRect.top <= y && rangeRect.bottom >= y) {
-                range.expand("word");
+                range.expand?.("word"); // doesn't exist in firefox
                 const ret = range.toString();
                 range.detach();
                 return (ret);
@@ -619,7 +619,7 @@ export function getWordAtPoint(elem: any, x: number, y: number): string | undefi
             currentPos += 1;
         }
     } else {
-        for (let childNode of elem.childNodes) {
+        for (const childNode of elem.childNodes) {
             const range = childNode.ownerDocument.createRange();
             range.selectNodeContents(childNode);
             const rangeRect = range.getBoundingClientRect();
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index e4aa639ff..0dbe0c917 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -231,7 +231,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
         } else {
             this.layoutDoc.nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
         }
-        this.layoutDoc._width = NumCast(this.layoutDoc._nativeWidth) * (NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) / NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]))
+        this.layoutDoc._width = NumCast(this.layoutDoc._nativeWidth) * (NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) / NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]));
     }
     settingsPanel() {
         const pageBtns = <>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index f733de86a..575fbcf2e 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,6 +1,6 @@
 import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked, ObservableMap } from "mobx";
 import { observer } from "mobx-react";
 import * as rp from 'request-promise';
 import { Dictionary } from "typescript-collections";
@@ -51,7 +51,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     private _playRegionDuration = 0;
     @observable static _showControls: boolean;
     @observable _marqueeing: number[] | undefined;
-    @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable _screenCapture = false;
     @observable _clicking = false;
     @observable _forceCreateYouTubeIFrame = false;
@@ -206,7 +206,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
         this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this._disposers.selection = reaction(() => this.props.isSelected(),
             selected => !selected && setTimeout(() => {
-                this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
                 this._savedAnnotations.clear();
             }));
         this._disposers.triggerVideo = reaction(
@@ -575,6 +575,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                         scrollTop={0}
                         rootDoc={this.rootDoc}
                         down={this._marqueeing}
+                        docView={this.props.docViewPath().lastElement()}
                         scaling={this.marqueeFitScaling}
                         containerOffset={this.marqueeOffset}
                         addDocument={this.addDocWithTimecode}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index bc20c3b47..578794e6e 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -436,7 +436,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         } else {
             this.layoutDoc.nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
         }
-        this.layoutDoc._width = NumCast(this.layoutDoc._nativeWidth) * (NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) / NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]))
+        this.layoutDoc._width = NumCast(this.layoutDoc._nativeWidth) * (NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) / NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]));
     }
     sidebarKey = () => this.fieldKey + "-sidebar";
     sidebarFiltersHeight = () => 50;
-- 
cgit v1.2.3-70-g09d2


From ef282cb41539045bc030b4058cee6c26a5cf0c03 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 16 Mar 2021 01:38:02 -0400
Subject: from last

---
 src/client/views/nodes/WebBox.tsx | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 578794e6e..f15a249da 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,7 +1,6 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Dictionary } from "typescript-collections";
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
@@ -13,10 +12,11 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { WebField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, getWordAtPoint, returnOne, returnTrue, returnZero, smoothScroll, Utils } from "../../../Utils";
+import { emptyFunction, getWordAtPoint, OmitKeys, returnOne, returnTrue, returnZero, smoothScroll, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { SnappingManager } from "../../util/SnappingManager";
 import { undoBatch } from "../../util/UndoManager";
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CollectionStackingView } from "../collections/CollectionStackingView";
@@ -25,6 +25,7 @@ import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
+import { LightboxView } from "../LightboxView";
 import { MarqueeAnnotator } from "../MarqueeAnnotator";
 import { AnchorMenu } from "../pdf/AnchorMenu";
 import { Annotation } from "../pdf/Annotation";
@@ -35,8 +36,6 @@ import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import { LinkDocPreview } from "./LinkDocPreview";
 import "./WebBox.scss";
 import React = require("react");
-import { LightboxView } from "../LightboxView";
-import { SnappingManager } from "../../util/SnappingManager";
 const htmlToText = require("html-to-text");
 
 type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -141,6 +140,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         if (iframe?.contentDocument) {
             iframe?.contentDocument.addEventListener("pointerdown", this.iframeDown);
             this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight);
+            setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
             if (this._initialScroll !== undefined && this._outerRef.current) {
                 this._outerRef.current.scrollTop = this._initialScroll;
                 this._initialScroll = undefined;
@@ -160,13 +160,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 }
             })));
             iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
-            iframe.contentDocument.addEventListener('scroll', () => !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false));
+            //iframe.contentDocument.addEventListener('scroll', () => !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false));
+            iframe.contentDocument.addEventListener('scroll', () => {
+                console.log("Scroll = " + this._iframe?.scrollTop)
+            }
+                , true);
         }
     }
 
     @action
     setDashScrollTop = (scrollTop: number, timeout: number = 250) => {
-        const iframeHeight = this._scrollHeight - this.panelHeight();
+        const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight());
         timeout = scrollTop > iframeHeight ? 0 : timeout;
         this._scrollTimer && clearTimeout(this._scrollTimer);
         this._scrollTimer = setTimeout(action(() => {
@@ -266,7 +270,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
     goTo = (scrollTop: number, duration: number) => {
         if (this._outerRef.current) {
-            const iframeHeight = this._scrollHeight - this.panelHeight();
+            const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight());
             scrollTop = scrollTop > iframeHeight + 50 ? iframeHeight : scrollTop;
             if (duration) {
                 smoothScroll(duration, [this._outerRef.current], scrollTop);
@@ -513,7 +517,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="webBox-annotationLayer" style={{ display: !this._savedAnnotations.size && !this.allAnnotations.length ? "none" : undefined, height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+        return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
             {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
                 <Annotation {...this.props} fieldKey={this.annotationKey} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)
             }
-- 
cgit v1.2.3-70-g09d2

