From 3fdee81100954e66f54c73661cb11967993ff467 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 15 Jul 2020 15:24:53 -0400
Subject: fixed tooltips to share a csss style.  added tooltips for richtext.

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

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4e5fdbfbb..56212a773 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -637,7 +637,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
 
     @action
     onZoomWheel = (e: React.WheelEvent) => {
-        if (this.active()) {
+        if (this.active(true)) {
             e.stopPropagation();
             if (e.ctrlKey) {
                 const curScale = Number(this._pdfViewer.currentScaleValue);
-- 
cgit v1.2.3-70-g09d2


From 60819bced2eb67282b77e25c77939dd45d01e7d8 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 17 Jul 2020 00:06:59 -0400
Subject: fixed pdf runtime errors.  fixed link lines to redraw durin
 animations.

---
 .../CollectionFreeFormLinkView.tsx                 | 26 +++++++++++++++-------
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  4 ++--
 3 files changed, 21 insertions(+), 11 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 0933d525a..6d44ac967 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -5,12 +5,10 @@ import { DocumentView } from "../../nodes/DocumentView";
 import "./CollectionFreeFormLinkView.scss";
 import React = require("react");
 import { DocumentType } from "../../../documents/DocumentTypes";
-import { observable, action, reaction, IReactionDisposer } from "mobx";
+import { observable, action, reaction, IReactionDisposer, trace, computed } from "mobx";
 import { StrCast, Cast, NumCast } from "../../../../fields/Types";
 import { Id } from "../../../../fields/FieldSymbols";
 import { SnappingManager } from "../../../util/SnappingManager";
-import { OverlayView } from "../../OverlayView";
-import { LinkEditor } from "../../linking/LinkEditor";
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -21,14 +19,20 @@ export interface CollectionFreeFormLinkViewProps {
 @observer
 export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
     @observable _opacity: number = 0;
+    @observable _start = 0;
     _anchorDisposer: IReactionDisposer | undefined;
-
+    _timeout: NodeJS.Timeout | undefined;
     componentWillUnmount() {
         this._anchorDisposer?.();
     }
+    @action
+    timeout = () => (Date.now() < this._start++ + 1000) && setTimeout(this.timeout, 25);
     componentDidMount() {
         this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)],
             action(() => {
+                this._start = Date.now();
+                this._timeout && clearTimeout(this._timeout);
+                this._timeout = setTimeout(this.timeout, 25);
                 if (SnappingManager.GetIsDragging() || !this.props.A.ContentDiv || !this.props.B.ContentDiv) return;
                 setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
                 setTimeout(action(() => (!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
@@ -101,9 +105,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         });
     }
 
-
-    render() {
-        if (SnappingManager.GetIsDragging() || !this.props.A.ContentDiv || !this.props.B.ContentDiv || !this.props.LinkDocs.length) return (null);
+    @computed get renderData() {
+        this._start;
+        if (SnappingManager.GetIsDragging() || !this.props.A.ContentDiv || !this.props.B.ContentDiv || !this.props.LinkDocs.length) {
+            return undefined;
+        }
         this.props.A.props.ScreenToLocalTransform().transform(this.props.B.props.ScreenToLocalTransform());
         const acont = this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont");
         const bcont = this.props.B.ContentDiv!.getElementsByClassName("linkAnchorBox-cont");
@@ -129,9 +135,13 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
 
         const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(this.props.LinkDocs[0].linkOffsetX);
         const textY = (pt1[1] + pt2[1]) / 2 + NumCast(this.props.LinkDocs[0].linkOffsetY);
+        return { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 };
+    }
 
+    render() {
+        if (!this.renderData) return (null);
+        const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
         return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
-
             <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
                 d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`} />
             <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 1c5825a8f..323da1233 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -268,7 +268,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
             if (!this._pdfjsRequested) {
                 this._pdfjsRequested = true;
                 const promise = Pdfjs.getDocument(pdfUrl.url.href).promise;
-                promise.then(action(pdf => { this._pdf = pdf; console.log("promise"); }));
+                promise.then(action(pdf => this._pdf = pdf));
 
             }
         }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 56212a773..30c51d9ca 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -262,7 +262,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         const eventBus = new PDFJSViewer.EventBus(true);
         eventBus._on("pagesinit", this.pagesinit);
         eventBus._on("pagerendered", action(() => this._showCover = this._showWaiting = false));
-        const pdfLinkService = new PDFJSViewer.PDFLinkService();
+        const pdfLinkService = new PDFJSViewer.PDFLinkService({ eventBus });
         const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
         this._pdfViewer = new PDFJSViewer.PDFViewer({
             container: this._mainCont.current,
@@ -392,7 +392,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     @action
     search = (searchString: string, fwd: boolean, clear: boolean = false) => {
         if (clear) {
-            this._pdfViewer.findController.executeCommand('reset', {});
+            this._pdfViewer.findController.executeCommand('reset', { query: "" });
         } else if (!searchString) {
             fwd ? this.nextAnnotation() : this.prevAnnotation();
         } else if (this._pdfViewer.pageViewsReady) {
-- 
cgit v1.2.3-70-g09d2


From 49fcb0f6613344fb62db618c0b466c6155c20eb0 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 27 Jul 2020 00:32:50 -0400
Subject: extended pdfviewer interactions to WebBox's.

---
 src/client/views/DocComponent.tsx  |   2 +-
 src/client/views/nodes/WebBox.scss |  21 ++-
 src/client/views/nodes/WebBox.tsx  | 262 ++++++++++++++++++++++++++++++++++---
 src/client/views/pdf/PDFViewer.tsx |   8 +-
 4 files changed, 265 insertions(+), 28 deletions(-)

(limited to 'src/client/views/pdf/PDFViewer.tsx')

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index cfabae8a1..9fd406407 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -122,7 +122,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         @action.bound
         removeDocument(doc: Doc | Doc[]): boolean {
             const docs = doc instanceof Doc ? [doc] : doc;
-            docs.map(doc => doc.annotationOn = undefined);
+            docs.map(doc => doc.isPushpin = doc.annotationOn = undefined);
             const targetDataDoc = this.dataDoc;
             const value = DocListCast(targetDataDoc[this.annotationKey]);
             const toRemove = value.filter(v => docs.includes(v));
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 4b2f3cc1a..f5c8745e7 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -5,6 +5,24 @@
     height:100%;
     position: relative;
     display: flex;
+
+    .pdfViewerDash-dragAnnotationBox {
+        position:absolute;
+        background-color: transparent;
+        opacity: 0.1;
+    }
+    .webBox-annotationLayer {
+        position: absolute;
+        transform-origin: left top;
+        top: 0;
+        width: 100%;
+        pointer-events: none;
+        mix-blend-mode: multiply; // bcz: makes text fuzzy!
+    }
+    .webBox-annotationBox {
+        position: absolute;
+        background-color: rgba(245, 230, 95, 0.616);
+    }
     .webBox-container, .webBox-container-dragging {
         transform-origin: top left;
         width: 100%;
@@ -46,9 +64,6 @@
             top: 0;
             left: 0;
             overflow: auto;
-            .webBox-innerContent { 
-                width:100%;
-            }
         }
         div.webBox-outerContent::-webkit-scrollbar-thumb {
             display:none;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 724044295..bc30b15e6 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,46 +1,62 @@
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { faStickyNote, faPen, faMousePointer } from '@fortawesome/free-solid-svg-icons';
-import { action, computed, observable, trace, IReactionDisposer, reaction, runInAction } from "mobx";
+import { faMousePointer, faPen, faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, FieldResult, DocListCast } from "../../../fields/Doc";
+import { Dictionary } from "typescript-collections";
+import * as WebRequest from 'web-request';
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
+import { Id } from "../../../fields/FieldSymbols";
 import { HtmlField } from "../../../fields/HtmlField";
 import { InkTool } from "../../../fields/InkField";
-import { makeInterface, listSpec } from "../../../fields/Schema";
-import { Cast, NumCast, BoolCast, StrCast } from "../../../fields/Types";
+import { List } from "../../../fields/List";
+import { listSpec, makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { WebField } from "../../../fields/URLField";
-import { Utils, returnOne, emptyFunction, returnZero } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
+import { TraceMobx } from "../../../fields/util";
+import { addStyleSheet, clearStyleSheetRules, emptyFunction, returnOne, returnZero, Utils } from "../../../Utils";
+import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
+import { undoBatch } from "../../util/UndoManager";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
 import { ViewBoxAnnotatableComponent } from "../DocComponent";
 import { DocumentDecorations } from "../DocumentDecorations";
+import Annotation from "../pdf/Annotation";
+import PDFMenu from "../pdf/PDFMenu";
+import { PdfViewerMarquee } from "../pdf/PDFViewer";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./WebBox.scss";
+import "../pdf/PDFViewer.scss";
 import React = require("react");
-import * as WebRequest from 'web-request';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { undoBatch } from "../../util/UndoManager";
-import { List } from "../../../fields/List";
 const htmlToText = require("html-to-text");
 
-library.add(faStickyNote);
-
 type WebDocument = makeInterface<[typeof documentSchema]>;
 const WebDocument = makeInterface(documentSchema);
 
 @observer
 export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
 
+    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+    static _annotationStyle: any = addStyleSheet();
+    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+    private _startX: number = 0;
+    private _startY: number = 0;
+    @observable private _marqueeX: number = 0;
+    @observable private _marqueeY: number = 0;
+    @observable private _marqueeWidth: number = 0;
+    @observable private _marqueeHeight: number = 0;
+    @observable private _marqueeing: boolean = false;
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
     get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; }
     set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; }
     @observable private _url: string = "hello";
     @observable private _pressX: number = 0;
     @observable private _pressY: number = 0;
+    @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+    private _selectionReactionDisposer?: IReactionDisposer;
     private _keyInput = React.createRef<HTMLInputElement>();
     private _longPressSecondsHack?: NodeJS.Timeout;
     private _outerRef = React.createRef<HTMLDivElement>();
@@ -75,6 +91,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             { fireImmediately: true }
         );
     });
+
     setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
     iframedown = (e: PointerEvent) => {
         this._setPreviewCursor?.(e.screenX, e.screenY, false);
@@ -89,6 +106,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField);
         runInAction(() => this._url = urlField?.url.toString() || "");
 
+
+        this._selectionReactionDisposer = 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, []));
+                    PDFMenu.Instance.fadeOut(true);
+                }
+            },
+            { fireImmediately: true });
+
         document.addEventListener("pointerup", this.onLongPressUp);
         document.addEventListener("pointermove", this.onLongPressMove);
         const field = Cast(this.rootDoc[this.props.fieldKey], WebField);
@@ -112,6 +140,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     }
 
     componentWillUnmount() {
+        this._selectionReactionDisposer?.();
         this._reactionDisposer?.();
         document.removeEventListener("pointerup", this.onLongPressUp);
         document.removeEventListener("pointermove", this.onLongPressMove);
@@ -267,11 +296,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         );
     }
 
+
     @action
     toggleCollapse = () => {
         this._collapsed = !this._collapsed;
     }
 
+
+
     _ignore = 0;
     onPreWheel = (e: React.WheelEvent) => {
         this._ignore = e.timeStamp;
@@ -284,6 +316,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             e.stopPropagation();
         }
     }
+
     onPostWheel = (e: React.WheelEvent) => {
         if (this._ignore !== e.timeStamp) {
             e.stopPropagation();
@@ -430,6 +463,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
 
         return (<>
             <div className={"webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !decInteracting ? "-interactive" : "")}
+                style={{ width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%" }}
                 onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
                 {view}
             </div>;
@@ -444,9 +478,188 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
             {this.urlEditor()}
         </>);
     }
+
+
+
+    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]); }
+    @computed get nonDocAnnotations() { return this.allAnnotations.filter(a => a.annotations); }
+
+    @undoBatch
+    @action
+    makeAnnotationDocument = (color: string): Opt<Doc> => {
+        if (this._savedAnnotations.size() === 0) return undefined;
+        const anno = this._savedAnnotations.values()[0][0];
+        const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.Document, title: "Annotation on " + this.Document.title });
+        if (anno.style.left) annoDoc.x = parseInt(anno.style.left);
+        if (anno.style.top) annoDoc.y = NumCast(this.layoutDoc._scrollTop) + parseInt(anno.style.top);
+        if (anno.style.height) annoDoc._height = parseInt(anno.style.height);
+        if (anno.style.width) annoDoc._width = parseInt(anno.style.width);
+        anno.remove();
+        this._savedAnnotations.clear();
+        return annoDoc;
+    }
+    @computed get annotationLayer() {
+        TraceMobx();
+        return <div className="webBox-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight) }} ref={this._annotationLayer}>
+            {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
+                <Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
+            }
+        </div>;
+    }
+    @action
+    createAnnotation = (div: HTMLDivElement, page: number) => {
+        if (this._annotationLayer.current) {
+            if (div.style.top) {
+                div.style.top = (parseInt(div.style.top)).toString();
+            }
+            this._annotationLayer.current.append(div);
+            div.style.backgroundColor = "#ACCEF7";
+            div.style.opacity = "0.5";
+            const savedPage = this._savedAnnotations.getValue(page);
+            if (savedPage) {
+                savedPage.push(div);
+                this._savedAnnotations.setValue(page, savedPage);
+            }
+            else {
+                this._savedAnnotations.setValue(page, [div]);
+            }
+        }
+    }
+
+    @action
+    highlight = (color: string) => {
+        // creates annotation documents for current highlights
+        const annotationDoc = this.makeAnnotationDocument(color);
+        annotationDoc && Doc.AddDocToList(this.props.Document, this.annotationKey, annotationDoc);
+        return annotationDoc;
+    }
+    /**
+     * This is temporary for creating annotations from highlights. It will
+     * start a drag event and create or put the necessary info into the drag event.
+     */
+    @action
+    startDrag = async (e: PointerEvent, ele: HTMLElement) => {
+        e.preventDefault();
+        e.stopPropagation();
+
+        const clipDoc = Doc.MakeAlias(this.dataDoc);
+        clipDoc._fitWidth = true;
+        clipDoc._width = this.marqueeWidth();
+        clipDoc._height = this.marqueeHeight();
+        clipDoc._scrollTop = this.marqueeY();
+        const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title });
+        Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
+        clipDoc.rootDocument = targetDoc;
+        targetDoc.layoutKey = "layout";
+        const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
+        if (annotationDoc) {
+            DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
+                dragComplete: e => {
+                    if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
+                        DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
+                        annotationDoc.isLinkButton = true;
+                        e.annoDragData.dropDocument.isPushpin = true;
+                        e.annoDragData.dropDocument.isLinkButton = true;
+                    }
+                }
+            });
+        }
+    }
+    @action
+    onMarqueeDown = (e: React.PointerEvent) => {
+        this._marqueeing = false;
+        if (!e.altKey && e.button === 0 && this.active(true)) {
+            // clear out old marquees and initialize menu for new selection
+            PDFMenu.Instance.StartDrag = this.startDrag;
+            PDFMenu.Instance.Highlight = this.highlight;
+            PDFMenu.Instance.Status = "pdf";
+            PDFMenu.Instance.fadeOut(true);
+            this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+            this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, []));
+            if ((e.target as any)?.parentElement.className === "textLayer") {
+                // start selecting text if mouse down on textLayer spans
+            }
+            else if (this._mainCont.current) {
+                // set marquee x and y positions to the spatially transformed position
+                const boundingRect = this._mainCont.current.getBoundingClientRect();
+                this._startX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1);
+                this._startY = (e.clientY - boundingRect.top) / boundingRect.height * (this.Document._nativeHeight || 1);
+                this._marqueeHeight = this._marqueeWidth = 0;
+                this._marqueeing = true;
+            }
+            document.removeEventListener("pointermove", this.onSelectMove);
+            document.addEventListener("pointermove", this.onSelectMove);
+            document.removeEventListener("pointerup", this.onSelectEnd);
+            document.addEventListener("pointerup", this.onSelectEnd);
+        }
+    }
+    @action
+    onSelectMove = (e: PointerEvent): void => {
+        if (this._marqueeing && this._mainCont.current) {
+            // transform positions and find the width and height to set the marquee to
+            const boundingRect = this._mainCont.current.getBoundingClientRect();
+            const curX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1);
+            const curY = (e.clientY - boundingRect.top) / boundingRect.height * (this.Document._nativeHeight || 1);
+            this._marqueeWidth = curX - this._startX;
+            this._marqueeHeight = curY - this._startY;
+            this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth);
+            this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight);
+            this._marqueeWidth = Math.abs(this._marqueeWidth);
+            this._marqueeHeight = Math.abs(this._marqueeHeight);
+            e.stopPropagation();
+            e.preventDefault();
+        }
+        else if (e.target && (e.target as any).parentElement === this._mainCont.current) {
+            e.stopPropagation();
+        }
+    }
+
+    @action
+    onSelectEnd = (e: PointerEvent): void => {
+        clearStyleSheetRules(WebBox._annotationStyle);
+        this._savedAnnotations.clear();
+        if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
+            const marquees = this._mainCont.current!.getElementsByClassName("pdfViewerDash-dragAnnotationBox");
+            if (marquees?.length) { // copy the marquee and convert it to a permanent annotation.
+                const style = (marquees[0] as HTMLDivElement).style;
+                const copy = document.createElement("div");
+                copy.style.left = style.left;
+                copy.style.top = style.top;
+                copy.style.width = style.width;
+                copy.style.height = style.height;
+                copy.style.border = style.border;
+                copy.style.opacity = style.opacity;
+                (copy as any).marqueeing = true;
+                copy.className = "webBox-annotationBox";
+                this.createAnnotation(copy, 0);
+            }
+
+            if (!e.ctrlKey) {
+                PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight };
+            }
+            PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
+        }
+        //this._marqueeing = false;
+
+        if (PDFMenu.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.616)");  // yellowish highlight color for highlighted text (should match PDFMenu's highlight color)
+        }
+        else {
+            PDFMenu.Instance.StartDrag = this.startDrag;
+            PDFMenu.Instance.Highlight = this.highlight;
+        }
+        document.removeEventListener("pointermove", this.onSelectMove);
+        document.removeEventListener("pointerup", this.onSelectEnd);
+    }
+
+    marqueeWidth = () => this._marqueeWidth;
+    marqueeHeight = () => this._marqueeHeight;
+    marqueeX = () => this._marqueeX;
+    marqueeY = () => this._marqueeY;
+    marqueeing = () => this._marqueeing;
     scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop));
     render() {
-        return (<div className="webBox" style={{ width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%" }}>
+        return (<div className="webBox" ref={this._mainCont} >
             <div className={`webBox-container`}
                 style={{
                     transform: `scale(${this.props.ContentScaling()})`,
@@ -458,8 +671,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 <base target="_blank" />
                 {this.content}
                 <div className={"webBox-outerContent"} ref={this._outerRef}
-                    style={{ pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none" }}
+                    style={{
+                        width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
+                        pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none"
+                    }}
                     onWheel={e => e.stopPropagation()}
+                    onPointerDown={this.onMarqueeDown}
                     onScroll={e => {
                         const iframe = this._iframeRef?.current?.contentDocument;
                         const outerFrame = this._outerRef.current;
@@ -473,7 +690,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                         }
                         //this._outerRef.current!.scrollTop !== this._scrollTop && (this._outerRef.current!.scrollTop = this._scrollTop)
                     }}>
-                    <div className={"webBox-innerContent"} style={{ height: NumCast(this.layoutDoc.scrollHeight), width: 4000 }}>
+                    <div className={"webBox-innerContent"} style={{
+                        height: NumCast(this.layoutDoc.scrollHeight),
+                        pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
+                    }}>
                         <CollectionFreeFormView {...this.props}
                             PanelHeight={this.props.PanelHeight}
                             PanelWidth={this.props.PanelWidth}
@@ -499,6 +719,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                         </CollectionFreeFormView>
                     </div>
                 </div>
+                {this.annotationLayer}
+                <PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} />
             </div >
         </div>);
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 30c51d9ca..5ed842ab7 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -521,7 +521,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         if (this._marqueeing) {
             if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
                 const marquees = this._mainCont.current!.getElementsByClassName("pdfViewerDash-dragAnnotationBox");
-                if (marquees && marquees.length) { // copy the marquee and convert it to a permanent annotation.
+                if (marquees?.length) { // copy the marquee and convert it to a permanent annotation.
                     const style = (marquees[0] as HTMLDivElement).style;
                     const copy = document.createElement("div");
                     copy.style.left = style.left;
@@ -544,7 +544,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
         }
         else {
             const sel = window.getSelection();
-            if (sel && sel.type === "Range") {
+            if (sel?.type === "Range") {
                 const selRange = sel.getRangeAt(0);
                 this.createTextAnnotation(sel, selRange);
                 PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
@@ -726,7 +726,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     }
 }
 
-interface PdfViewerMarqueeProps {
+export interface PdfViewerMarqueeProps {
     isMarqueeing: () => boolean;
     width: () => number;
     height: () => number;
@@ -735,7 +735,7 @@ interface PdfViewerMarqueeProps {
 }
 
 @observer
-class PdfViewerMarquee extends React.Component<PdfViewerMarqueeProps> {
+export class PdfViewerMarquee extends React.Component<PdfViewerMarqueeProps> {
     render() {
         return !this.props.isMarqueeing() ? (null) : <div className="pdfViewerDash-dragAnnotationBox"
             style={{
-- 
cgit v1.2.3-70-g09d2

