From 135e252902a3ca93e95672602122afb3be6cd015 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Thu, 20 Jun 2019 10:43:58 -0400
Subject: basic pdf snippetting

---
 src/client/views/pdf/PDFMenu.tsx | 45 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 42 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 39b15fb11..a8e176858 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -5,6 +5,8 @@ import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { emptyFunction } from "../../../Utils";
 import { Doc } from "../../../new_fields/Doc";
+import { DragManager } from "../../util/DragManager";
+import { DocUtils } from "../../documents/Documents";
 
 @observer
 export default class PDFMenu extends React.Component {
@@ -16,19 +18,23 @@ export default class PDFMenu extends React.Component {
     @observable private _transition: string = "opacity 0.5s";
     @observable private _transitionDelay: string = "";
 
-    @observable public Pinned: boolean = false;
 
     StartDrag: (e: PointerEvent) => void = emptyFunction;
     Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
     Delete: () => void = emptyFunction;
+    Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
 
     @observable public Highlighting: boolean = false;
-    @observable public Status: "pdf" | "annotation" | "" = "";
+    @observable public Status: "pdf" | "annotation" | "snippet" | "" = "";
+    @observable public Pinned: boolean = false;
+
+    public Marquee: { left: number; top: number; width: number; height: number; } | undefined;
 
     private _offsetY: number = 0;
     private _offsetX: number = 0;
     private _mainCont: React.RefObject<HTMLDivElement>;
     private _dragging: boolean = false;
+    private _snippetButton: React.RefObject<HTMLButtonElement>;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -36,6 +42,7 @@ export default class PDFMenu extends React.Component {
         PDFMenu.Instance = this;
 
         this._mainCont = React.createRef();
+        this._snippetButton = React.createRef();
     }
 
     pointerDown = (e: React.PointerEvent) => {
@@ -171,13 +178,45 @@ export default class PDFMenu extends React.Component {
         e.preventDefault();
     }
 
+    snippetStart = (e: React.PointerEvent) => {
+        document.removeEventListener("pointermove", this.snippetDrag);
+        document.addEventListener("pointermove", this.snippetDrag);
+        document.removeEventListener("pointerup", this.snippetEnd);
+        document.addEventListener("pointerup", this.snippetEnd);
+
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    snippetDrag = (e: PointerEvent) => {
+        e.stopPropagation();
+        e.preventDefault();
+        if (this._dragging) {
+            return;
+        }
+        this._dragging = true;
+
+        if (this.Marquee) {
+            this.Snippet(this.Marquee);
+        }
+    }
+
+    snippetEnd = (e: PointerEvent) => {
+        this._dragging = false;
+        document.removeEventListener("pointermove", this.snippetDrag);
+        document.removeEventListener("pointerup", this.snippetEnd);
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
     render() {
-        let buttons = this.Status === "pdf" ? [
+        let buttons = this.Status === "pdf" || this.Status === "snippet" ? [
             <button className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked}
                 style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
                 <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
             </button>,
             <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
+            this.Status === "snippet" ? <button className="pdfMenu-button" title="Drag to Snippetize Selection" onPointerDown={this.snippetStart} ref={this._snippetButton}><FontAwesomeIcon icon="cut" size="lg" /></button> : undefined,
             <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin}
                 style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
                 <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this.Pinned ? "rotate(45deg)" : "" }} />
-- 
cgit v1.2.3-70-g09d2


From e910a6cd56936234e451994c893d8592e430f828 Mon Sep 17 00:00:00 2001
From: yipstanley <stanley_yip@brown.edu>
Date: Tue, 25 Jun 2019 13:54:20 -0400
Subject: pdf view specs fixes/changes

---
 src/client/views/collections/CollectionPDFView.tsx |  4 ++
 src/client/views/nodes/DocumentView.tsx            | 13 +++-
 src/client/views/nodes/PDFBox.tsx                  | 44 ++++++++++---
 src/client/views/pdf/PDFMenu.scss                  |  7 ++
 src/client/views/pdf/PDFMenu.tsx                   | 55 +++++++++++-----
 src/client/views/pdf/PDFViewer.tsx                 | 75 ++++++++++++++++------
 6 files changed, 152 insertions(+), 46 deletions(-)

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

diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index b2d016934..31a73ab36 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -43,6 +43,10 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
         );
     }
 
+    componentWillUnmount() {
+        this._reactionDisposer && this._reactionDisposer();
+    }
+
     public static LayoutString(fieldKey: string = "data") {
         return FieldView.LayoutString(CollectionPDFView, fieldKey);
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 522c37989..f5a3f4d5d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -117,6 +117,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
 
+    @observable private _opacity: number = this.Document.opacity ? NumCast(this.Document.opacity) : 1;
+
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
     @computed get topMost(): boolean { return this.props.isTopMost; }
@@ -136,6 +138,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     _animateToIconDisposer?: IReactionDisposer;
     _reactionDisposer?: IReactionDisposer;
+    _opacityDisposer?: IReactionDisposer;
     @action
     componentDidMount() {
         if (this._mainCont.current) {
@@ -160,6 +163,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false)
             , { fireImmediately: true });
         DocumentManager.Instance.DocumentViews.push(this);
+        this._opacityDisposer = reaction(
+            () => NumCast(this.props.Document.opacity),
+            () => {
+                runInAction(() => this._opacity = NumCast(this.props.Document.opacity));
+            }
+        );
     }
 
     animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => {
@@ -201,6 +210,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this._reactionDisposer) this._reactionDisposer();
         if (this._animateToIconDisposer) this._animateToIconDisposer();
         if (this._dropDisposer) this._dropDisposer();
+        if (this._opacityDisposer) this._opacityDisposer();
         DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
     }
 
@@ -559,7 +569,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                     background: this.Document.backgroundColor || "",
                     width: nativeWidth,
                     height: nativeHeight,
-                    transform: `scale(${scaling}, ${scaling})`
+                    transform: `scale(${scaling}, ${scaling})`,
+                    opacity: this._opacity
                 }}
                 onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index a129e89b9..c0f2d313a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -18,9 +18,9 @@ import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
 import { CompileScript } from '../../util/Scripting';
-import { ScriptField } from '../../../fields/ScriptField';
 import { Flyout, anchorPoints } from '../DocumentDecorations';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { ScriptField } from '../../../new_fields/ScriptField';
 
 type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
 const PdfDocument = makeInterface(positionSchema, pageSchema);
@@ -37,6 +37,9 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
     private _keyValue: string = "";
     private _valueValue: string = "";
     private _scriptValue: string = "";
+    private _keyRef: React.RefObject<HTMLInputElement>;
+    private _valueRef: React.RefObject<HTMLInputElement>;
+    private _scriptRef: React.RefObject<HTMLInputElement>;
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -51,10 +54,9 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
             }
         );
 
-        let script = CompileScript("return this.page === 0", { params: { this: Doc.name } });
-        if (script.compiled) {
-            this.props.Document.filterScript = new ScriptField(script);
-        }
+        this._keyRef = React.createRef();
+        this._valueRef = React.createRef();
+        this._scriptRef = React.createRef();
     }
 
     componentDidMount() {
@@ -99,17 +101,21 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         this._valueValue = e.currentTarget.value;
     }
 
+    @action
     private newScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this._scriptValue = e.currentTarget.value;
     }
 
-    private applyFilter = (e: React.MouseEvent<HTMLButtonElement>) => {
+    private applyFilter = () => {
         let scriptText = "";
         if (this._scriptValue.length > 0) {
             scriptText = this._scriptValue;
         } else if (this._keyValue.length > 0 && this._valueValue.length > 0) {
             scriptText = `return this.${this._keyValue} === ${this._valueValue}`;
         }
+        else {
+            scriptText = "return true";
+        }
         let script = CompileScript(scriptText, { params: { this: Doc.name } });
         if (script.compiled) {
             this.props.Document.filterScript = new ScriptField(script);
@@ -121,6 +127,22 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
         this._flyout = !this._flyout;
     }
 
+    @action
+    private resetFilters = () => {
+        this._keyValue = this._valueValue = "";
+        this._scriptValue = "return true";
+        if (this._keyRef.current) {
+            this._keyRef.current.value = "";
+        }
+        if (this._valueRef.current) {
+            this._valueRef.current.value = "";
+        }
+        if (this._scriptRef.current) {
+            this._scriptRef.current.value = "";
+        }
+        this.applyFilter();
+    }
+
     settingsPanel() {
         return !this.props.active() ? (null) :
             (
@@ -144,14 +166,18 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
                         </div>
                         <div className="pdfBox-settingsFlyout-kvpInput">
                             <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange}
-                                style={{ gridColumn: 1 }} />
+                                style={{ gridColumn: 1 }} ref={this._keyRef} />
                             <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange}
-                                style={{ gridColumn: 3 }} />
+                                style={{ gridColumn: 3 }} ref={this._valueRef} />
                         </div>
                         <div className="pdfBox-settingsFlyout-kvpInput">
-                            <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} />
+                            <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} />
                         </div>
                         <div className="pdfBox-settingsFlyout-kvpInput">
+                            <button style={{ gridColumn: 1 }} onClick={this.resetFilters}>
+                                <FontAwesomeIcon style={{ color: "white" }} icon="trash" size="lg" />
+                                &nbsp; Reset Filters
+                            </button>
                             <button style={{ gridColumn: 3 }} onClick={this.applyFilter}>
                                 <FontAwesomeIcon style={{ color: "white" }} icon="check" size="lg" />
                                 &nbsp; Apply
diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss
index 22868082a..a4624b1f6 100644
--- a/src/client/views/pdf/PDFMenu.scss
+++ b/src/client/views/pdf/PDFMenu.scss
@@ -22,4 +22,11 @@
         height: 100%;
         transition: width .2s;
     }
+
+    .pdfMenu-addTag {
+        display: grid;
+        width: 200px;
+        padding: 5px;
+        grid-template-columns: 90px 20px 90px;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index a8e176858..e58ad9ccc 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -1,9 +1,9 @@
 import React = require("react");
 import "./PDFMenu.scss";
-import { observable, action } from "mobx";
+import { observable, action, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { emptyFunction } from "../../../Utils";
+import { emptyFunction, returnZero, returnTrue, returnFalse } from "../../../Utils";
 import { Doc } from "../../../new_fields/Doc";
 import { DragManager } from "../../util/DragManager";
 import { DocUtils } from "../../documents/Documents";
@@ -23,6 +23,7 @@ export default class PDFMenu extends React.Component {
     Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction;
     Delete: () => void = emptyFunction;
     Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
+    AddTag: (key: string, value: string) => boolean = returnFalse;
 
     @observable public Highlighting: boolean = false;
     @observable public Status: "pdf" | "annotation" | "snippet" | "" = "";
@@ -35,6 +36,9 @@ export default class PDFMenu extends React.Component {
     private _mainCont: React.RefObject<HTMLDivElement>;
     private _dragging: boolean = false;
     private _snippetButton: React.RefObject<HTMLButtonElement>;
+    @observable private _keyValue: string = "";
+    @observable private _valueValue: string = "";
+    @observable private _added: boolean = false;;
 
     constructor(props: Readonly<{}>) {
         super(props);
@@ -209,35 +213,56 @@ export default class PDFMenu extends React.Component {
         e.preventDefault();
     }
 
+    @action
+    keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._keyValue = e.currentTarget.value;
+    }
+
+    @action
+    valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this._valueValue = e.currentTarget.value;
+    }
+
+    @action
+    addTag = (e: React.PointerEvent) => {
+        if (this._keyValue.length > 0 && this._valueValue.length > 0) {
+            this._added = this.AddTag(this._keyValue, this._valueValue);
+
+            setTimeout(
+                () => {
+                    runInAction(() => {
+                        this._added = false;
+                    });
+                }, 1000
+            );
+        }
+    }
+
     render() {
         let buttons = this.Status === "pdf" || this.Status === "snippet" ? [
-            <button className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked}
+            <button className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked} key="1"
                 style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
                 <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
             </button>,
-            <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
+            <button className="pdfMenu-button" title="Drag to Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" key="2" /></button>,
             this.Status === "snippet" ? <button className="pdfMenu-button" title="Drag to Snippetize Selection" onPointerDown={this.snippetStart} ref={this._snippetButton}><FontAwesomeIcon icon="cut" size="lg" /></button> : undefined,
-            <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin}
+            <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin} key="3"
                 style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
                 <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this.Pinned ? "rotate(45deg)" : "" }} />
             </button>
         ] : [
-                <button className="pdfMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}><FontAwesomeIcon icon="trash-alt" size="lg" /></button>
+                <button className="pdfMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}><FontAwesomeIcon icon="trash-alt" size="lg" key="1" /></button>,
+                <div className="pdfMenu-addTag" key="2">
+                    <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
+                    <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
+                </div>,
+                <button className="pdfMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}><FontAwesomeIcon style={{ transition: "all .2s" }} color={this._added ? "#42f560" : "white"} icon="check" size="lg" key="3" /></button>,
             ];
 
         return (
             <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
                 style={{ left: this._left, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}>
                 {buttons}
-                {/* <button className="pdfMenu-button" title="Highlight" onClick={this.highlightClicked}
-                    style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
-                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
-                </button>
-                <button className="pdfMenu-button" title="Annotate" onPointerDown={this.pointerDown}><FontAwesomeIcon icon="comment-alt" size="lg" /></button>
-                <button className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin}
-                    style={this._pinned ? { backgroundColor: "#121212" } : {}}>
-                    <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this._pinned ? "rotate(45deg)" : "" }} />
-                </button> */}
                 <div className="pdfMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} />
             </div >
         );
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 1eab13bc5..3df7dd77b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -20,8 +20,8 @@ import "./PDFViewer.scss";
 import React = require("react");
 import PDFMenu from "./PDFMenu";
 import { UndoManager } from "../../util/UndoManager";
-import { ScriptField } from "../../../fields/ScriptField";
 import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
+import { ScriptField } from "../../../new_fields/ScriptField";
 
 export const scale = 2;
 interface IPDFViewerProps {
@@ -63,8 +63,6 @@ interface IViewerProps {
     url: string;
 }
 
-const PinRadius = 25;
-
 /**
  * Handles rendering and virtualization of the pdf
  */
@@ -85,14 +83,18 @@ class Viewer extends React.Component<IViewerProps> {
     private _annotationReactionDisposer?: IReactionDisposer;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _filterReactionDisposer?: IReactionDisposer;
+    private _viewer: React.RefObject<HTMLDivElement>;
+    private _mainCont: React.RefObject<HTMLDivElement>;
+    private _textContent: Pdfjs.TextContent[] = [];
 
-    @action
-        constructor(props: IViewerProps) {
-            super(props);
+    constructor(props: IViewerProps) {
+        super(props);
 
-            let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
-            this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");;
-        }
+        let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
+        this._script = scriptfield ? scriptfield.script : CompileScript("return true");
+        this._viewer = React.createRef();
+        this._mainCont = React.createRef();
+    }
 
     componentDidUpdate = (prevProps: IViewerProps) => {
         if (this.scrollY !== prevProps.scrollY) {
@@ -118,21 +120,37 @@ class Viewer extends React.Component<IViewerProps> {
 
         if (this.props.parent.props.ContainingCollectionView) {
             this._filterReactionDisposer = reaction(
-                () => this.props.parent.Document.filterScript || this.props.parent.props.ContainingCollectionView!.props.Document.filterScript,
+                () => this.props.parent.Document.filterScript,
                 () => {
                     runInAction(() => {
                         let scriptfield = Cast(this.props.parent.Document.filterScript, ScriptField);
-                        this._script = scriptfield ? CompileScript(scriptfield.scriptString, { params: { this: Doc.name } }) : CompileScript("return true");;
+                        this._script = scriptfield ? scriptfield.script : CompileScript("return true");
+                        if (this.props.parent.props.ContainingCollectionView) {
+                            let ccvAnnos = DocListCast(this.props.parent.props.ContainingCollectionView.props.Document.annotations);
+                            ccvAnnos.forEach(d => {
+                                if (this._script && this._script.compiled) {
+                                    let run = this._script.run(d);
+                                    if (run.success) {
+                                        d.opacity = run.result ? 1 : 0;
+                                    }
+                                }
+                            })
+                        }
                     });
                 }
             );
         }
+
+        if (this._mainCont.current) {
+            this._dropDisposer = this._mainCont.current && DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
+        }
     }
 
     componentWillUnmount = () => {
         this._reactionDisposer && this._reactionDisposer();
         this._annotationReactionDisposer && this._annotationReactionDisposer();
         this._filterReactionDisposer && this._filterReactionDisposer();
+        this._dropDisposer && this._dropDisposer();
     }
 
     @action
@@ -140,10 +158,14 @@ class Viewer extends React.Component<IViewerProps> {
         if (this._pageSizes.length === 0) {
             let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
             this._isPage = Array<string>(this.props.pdf.numPages);
+            this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
             for (let i = 0; i < this.props.pdf.numPages; i++) {
                 await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
                     // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
                     let x = page.getViewport(scale);
+                    page.getTextContent().then((text: Pdfjs.TextContent) => {
+                        this._textContent[i] = text;
+                    })
                     pageSizes[i] = { width: x.width, height: x.height };
                 }));
             }
@@ -162,13 +184,6 @@ class Viewer extends React.Component<IViewerProps> {
         }
     }
 
-    private mainCont = (div: HTMLDivElement | null) => {
-        this._dropDisposer && this._dropDisposer();
-        if (div) {
-            this._dropDisposer = div && DragManager.MakeDropTarget(div, { handlers: { drop: this.drop.bind(this) } });
-        }
-    }
-
     makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => {
         let annoDocs: Doc[] = [];
         let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {});
@@ -222,6 +237,7 @@ class Viewer extends React.Component<IViewerProps> {
     pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
         this.props.loaded(page.width, page.height, this.props.pdf.numPages);
     }
+
     @action
     getPlaceholderPage = (page: number) => {
         if (this._isPage[page] !== "none") {
@@ -232,6 +248,7 @@ class Viewer extends React.Component<IViewerProps> {
             );
         }
     }
+
     @action
     getRenderedPage = (page: number) => {
         if (this._isPage[page] !== "page") {
@@ -374,11 +391,16 @@ class Viewer extends React.Component<IViewerProps> {
         return res;
     }
 
+    pointerDown = () => {
+
+        let x = this._textContent;
+    }
+
     render() {
         let compiled = this._script;
         return (
-            <div ref={this.mainCont} style={{ pointerEvents: "all" }}>
-                <div className="viewer">
+            <div ref={this._mainCont} style={{ pointerEvents: "all" }} onPointerDown={this.pointerDown}>
+                <div className="viewer" ref={this._viewer}>
                     {this._visibleElements}
                 </div>
                 <div className="pdfViewer-annotationLayer"
@@ -472,12 +494,23 @@ class RegionAnnotation extends React.Component<IAnnotationProps> {
         }
         if (e.button === 2) {
             PDFMenu.Instance.Status = "annotation";
-            PDFMenu.Instance.Delete = this.deleteAnnotation;
+            PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
             PDFMenu.Instance.Pinned = false;
+            PDFMenu.Instance.AddTag = this.addTag.bind(this);
             PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
         }
     }
 
+    addTag = (key: string, value: string): boolean => {
+        let group = FieldValue(Cast(this.props.document.group, Doc));
+        if (group) {
+            let valNum = parseInt(value);
+            group[key] = isNaN(valNum) ? value : valNum;
+            return true;
+        }
+        return false;
+    }
+
     render() {
         return (
             <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
-- 
cgit v1.2.3-70-g09d2

