aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DocumentManager.ts4
-rw-r--r--src/client/views/collections/CollectionPDFView.scss7
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx3
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx1
-rw-r--r--src/client/views/nodes/PDFBox.tsx44
-rw-r--r--src/client/views/pdf/PDFViewer.scss56
-rw-r--r--src/client/views/pdf/PDFViewer.tsx300
-rw-r--r--src/client/views/pdf/Page.scss36
-rw-r--r--src/client/views/pdf/Page.tsx293
-rw-r--r--src/debug/Test.tsx35
10 files changed, 287 insertions, 492 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 5ade2ebb3..e60ab09bb 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -132,9 +132,7 @@ export class DocumentManager {
let doc = Doc.GetProto(docDelegate);
const contextDoc = await Cast(doc.annotationOn, Doc);
if (contextDoc) {
- const page = NumCast(doc.page, linkPage || 0);
- const curPage = NumCast(contextDoc.curPage, page);
- if (page !== curPage) contextDoc.curPage = page;
+ contextDoc.panY = doc.y;
}
let docView: DocumentView | null;
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
index 50201bae8..a853e5ca6 100644
--- a/src/client/views/collections/CollectionPDFView.scss
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -29,12 +29,7 @@
top: 0;
left: 0;
z-index: -1;
-}
-
-.collectionPdfView-cont-dragging {
- span {
- user-select: none;
- }
+ overflow: hidden !important;
}
.collectionPdfView-backward {
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 8eda4d9ee..8f052db3c 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,4 +1,4 @@
-import { computed } from "mobx";
+import { computed, trace } from "mobx";
import { observer } from "mobx-react";
import { Id } from "../../../new_fields/FieldSymbols";
import { emptyFunction } from "../../../Utils";
@@ -46,6 +46,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
}
render() {
+ trace();
return (
<CollectionBaseView {...this.props} className={"collectionPdfView-cont"} onContextMenu={this.onContextMenu}>
{this.subView}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index b30055071..bcb26b4c4 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -103,6 +103,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
finalPanelHeight = () => { return this.dataProvider ? this.dataProvider.height : this.panelHeight(); }
render() {
+ trace();
return (
<div className="collectionFreeFormDocumentView-container"
style={{
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d15f2b82c..12a5bc492 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -26,11 +26,12 @@ const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@observer
export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocument) {
public static LayoutString() { return FieldView.LayoutString(PDFBox); }
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _reactionDisposer?: IReactionDisposer;
private _keyValue: string = "";
private _valueValue: string = "";
private _scriptValue: string = "";
+ @observable private _searching: boolean = false;
+ private _pdfViewer: PDFViewer | undefined;
private _keyRef: React.RefObject<HTMLInputElement> = React.createRef();
private _valueRef: React.RefObject<HTMLInputElement> = React.createRef();
private _scriptRef: React.RefObject<HTMLInputElement> = React.createRef();
@@ -58,37 +59,45 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
this._reactionDisposer && this._reactionDisposer();
}
+ public search(string: string) {
+ this._pdfViewer && this._pdfViewer.search(string);
+ }
+
+ setPdfViewer = (pdfViewer: PDFViewer) => {
+ this._pdfViewer = pdfViewer;
+ }
+
public GetPage() {
- return 1;//Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
+ return Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
}
@action
public BackPage() {
- // let cp = Math.ceil((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
- // cp = cp - 1;
- // if (cp > 0) {
- // this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
- // }
+ let cp = Math.ceil((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
+ cp = cp - 1;
+ if (cp > 0) {
+ this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
+ }
}
@action
public GotoPage = (p: number) => {
- // if (p > 0 && p <= NumCast(this.dataDoc.numPages)) {
- // this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0);
- // }
+ if (p > 0 && p <= NumCast(this.dataDoc.numPages)) {
+ this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0);
+ }
}
@action
public ForwardPage() {
- // let cp = this.GetPage() + 1;
- // if (cp <= NumCast(this.dataDoc.numPages)) {
- // this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
- // }
+ let cp = this.GetPage() + 1;
+ if (cp <= NumCast(this.dataDoc.numPages)) {
+ this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
+ }
}
@action
setPanY = (y: number) => {
- //this.Document.panY = y;
+ this.Document.panY = y;
}
@action
@@ -99,7 +108,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
scrollTo = (y: number) => {
- this._mainCont.current && this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current.offsetHeight / 2), 0), behavior: "auto" });
+
}
private resetFilters = () => {
@@ -172,13 +181,14 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
return (!(pdfUrl instanceof PdfField) || !this._pdf ?
<div>{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}</div> :
- <div className={classname} ref={this._mainCont} onWheel={(e: React.WheelEvent) => e.stopPropagation()} onPointerDown={(e: React.PointerEvent) => {
+ <div className={classname} onWheel={(e: React.WheelEvent) => e.stopPropagation()} onPointerDown={(e: React.PointerEvent) => {
let hit = document.elementFromPoint(e.clientX, e.clientY);
if (hit && hit.localName === "span") {
e.button === 0 && e.stopPropagation();
}
}}>
<PDFViewer pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded}
+ setPdfViewer={this.setPdfViewer}
Document={this.props.Document} DataDoc={this.dataDoc}
addDocTab={this.props.addDocTab} GoToPage={this.GotoPage}
pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index fdfbde457..4388bc64c 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -1,39 +1,31 @@
-
.pdfViewer-viewer {
- pointer-events:inherit;
+ pointer-events: inherit;
width: 100%;
- height:100%;
+ height: 100%;
position: absolute;
- overflow: scroll;
- .pdfViewer-visibleElements {
- .pdfPage-cont {
- .pdfPage-textLayer {
- div {
- user-select: text;
- }
- span {
- color: transparent;
- position: absolute;
- white-space: pre;
- cursor: text;
- -webkit-transform-origin: 0% 0%;
- transform-origin: 0% 0%;
- }
- }
- }
- }
+ overflow-y: scroll;
+ overflow-x: hidden;
+
.page {
position: relative;
}
+
.pdfViewer-text {
transform-origin: top left;
}
+ .pdfViewer-dragAnnotationBox {
+ position:absolute;
+ background-color: transparent;
+ opacity: 0.1;
+ }
+
.pdfViewer-annotationLayer {
position: absolute;
top: 0;
width: 100%;
pointer-events: none;
+
.pdfPage-annotationBox {
position: absolute;
background-color: red;
@@ -41,26 +33,6 @@
}
}
- .pdfViewer-overlayCont {
- position: absolute;
- width: 100%;
- height: 100px;
- background: #121721;
- bottom: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 20px;
- overflow: hidden;
- transition: left .5s;
- .pdfViewer-overlaySearchBar {
- width: 20%;
- height: 100%;
- font-size: 30px;
- padding: 5px;
- }
- }
-
.pdfViewer-overlayButton {
border-bottom-left-radius: 50%;
display: flex;
@@ -95,4 +67,4 @@
.pdfViewer-overlayButton:hover {
background: none;
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4f1d3f07f..899a0f5aa 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,29 +1,27 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
-import * as rp from "request-promise";
import { Dictionary } from "typescript-collections";
import { Doc, DocListCast, FieldResult, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils, numberRange } from "../../../Utils";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { numberRange } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from "../../documents/Documents";
import { KeyCodes } from "../../northstar/utils/KeyCodes";
-import { CompileScript, CompiledScript } from "../../util/Scripting";
+import { DragManager } from "../../util/DragManager";
+import { CompiledScript, CompileScript } from "../../util/Scripting";
import Annotation from "./Annotation";
-import Page from "./Page";
+import PDFMenu from "./PDFMenu";
import "./PDFViewer.scss";
import React = require("react");
import requestPromise = require("request-promise");
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
-export const scale = 2;
-
interface IViewerProps {
pdf: Pdfjs.PDFDocumentProxy;
url: string;
@@ -38,6 +36,7 @@ interface IViewerProps {
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
pinToPres: (document: Doc) => void;
addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
+ setPdfViewer: (view: PDFViewer) => void;
}
/**
@@ -45,13 +44,17 @@ interface IViewerProps {
*/
@observer
export class PDFViewer extends React.Component<IViewerProps> {
- @observable public _pageSizes: { width: number, height: number }[] = [];
+ @observable private _pageSizes: { width: number, height: number }[] = [];
@observable private _annotations: Doc[] = [];
@observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
@observable private _script: CompiledScript = CompileScript("return true") as CompiledScript;
- @observable private _searching: boolean = false;
@observable private Index: number = -1;
+ @observable private _marqueeX: number = 0;
+ @observable private _marqueeY: number = 0;
+ @observable private _marqueeWidth: number = 0;
+ @observable private _marqueeHeight: number = 0;
+ private _resizeReaction: IReactionDisposer | undefined;
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _reactionDisposer?: IReactionDisposer;
private _annotationReactionDisposer?: IReactionDisposer;
@@ -59,9 +62,11 @@ export class PDFViewer extends React.Component<IViewerProps> {
private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
public _pdfViewer: any;
- private _pdfFindController: any;
- private _searchString: string = "";
private _selectionText: string = "";
+ private _marquee: React.RefObject<HTMLDivElement> = React.createRef();
+ private _marqueeing: boolean = false;
+ private _startY: number = 0;
+ private _startX: number = 0;
@computed get allAnnotations() {
return DocListCast(this.props.fieldExtensionDoc.annotations).filter(
@@ -73,6 +78,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
componentDidMount = async () => {
+ this.props.setPdfViewer(this);
await this.initialLoad();
this._annotationReactionDisposer = reaction(
@@ -92,14 +98,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
}),
{ fireImmediately: true }
);
+ this._reactionDisposer = reaction(
+ () => this.props.Document.panY,
+ () => this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.props.Document.panY) || 0, behavior: "auto" })
+ );
document.removeEventListener("copy", this.copy);
document.addEventListener("copy", this.copy);
-
- setTimeout(() => this.toggleSearch(undefined as any), 1000);
+ this.setupPdfJsViewer();
}
componentWillUnmount = () => {
+ this._resizeReaction && this._resizeReaction();
this._reactionDisposer && this._reactionDisposer();
this._annotationReactionDisposer && this._annotationReactionDisposer();
this._filterReactionDisposer && this._filterReactionDisposer();
@@ -123,14 +133,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
}
- searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
-
- pageLoaded = (page: Pdfjs.PDFPageViewport): void => this.props.loaded(page.width, page.height, this.props.pdf.numPages);
-
setSelectionText = (text: string) => this._selectionText = text;
- getIndex = () => this.Index;
-
@action
initialLoad = async () => {
if (this._pageSizes.length === 0) {
@@ -148,6 +152,28 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
@action
+ setupPdfJsViewer = () => {
+ this._reactionDisposer = reaction(() => this.props.Document[WidthSym](),
+ () => this._pdfViewer.currentScaleValue = (this.props.Document[WidthSym]() / this._pageSizes[0].width));
+ document.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = (this.props.Document[WidthSym]() / this._pageSizes[0].width));
+ document.addEventListener("pagerendered", () => console.log("rendered"));
+ var pdfLinkService = new PDFJSViewer.PDFLinkService();
+ let pdfFindController = new PDFJSViewer.PDFFindController({
+ linkService: pdfLinkService,
+ });
+ this._pdfViewer = new PDFJSViewer.PDFViewer({
+ container: this._mainCont.current,
+ viewer: this._viewer.current,
+ linkService: pdfLinkService,
+ findController: pdfFindController,
+ renderer: "svg"
+ });
+ pdfLinkService.setViewer(this._pdfViewer);
+ pdfLinkService.setDocument(this.props.pdf, null);
+ this._pdfViewer.setDocument(this.props.pdf);
+ }
+
+ @action
makeAnnotationDocument = (sourceDoc: Doc | undefined, color: string, createLink: boolean = true): Doc => {
let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {});
let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc);
@@ -169,6 +195,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
anno.remove();
this.props.addDocument && this.props.addDocument(annoDoc, false);
mainAnnoDoc = annoDoc;
+ mainAnnoDocProto.y = annoDoc.y;
mainAnnoDocProto = Doc.GetProto(annoDoc);
} else {
this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => {
@@ -262,7 +289,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
createAnnotation = (div: HTMLDivElement, page: number) => {
if (this._annotationLayer.current) {
if (div.style.top) {
- div.style.top = (parseInt(div.style.top) + this.getScrollFromPage(page)).toString();
+ div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString();
}
this._annotationLayer.current.append(div);
let savedPage = this._savedAnnotations.getValue(page);
@@ -279,7 +306,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
@action
search = (searchString: string) => {
if (this._pdfViewer._pageViewsReady) {
- this._pdfFindController.executeCommand('findagain', {
+ this._pdfViewer.findController.executeCommand('findagain', {
caseSensitive: false,
findPrevious: undefined,
highlightAll: true,
@@ -289,66 +316,219 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
else if (this._mainCont.current) {
let executeFind = () => {
- this._pdfFindController.executeCommand('find', {
+ this._pdfViewer.findController.executeCommand('find', {
caseSensitive: false,
findPrevious: undefined,
highlightAll: true,
phraseSearch: true,
query: searchString
});
- }
+ };
this._mainCont.current.addEventListener("pagesloaded", executeFind);
this._mainCont.current.addEventListener("pagerendered", executeFind);
}
}
+ @action
+ onPointerDown = (e: React.PointerEvent): void => {
+ // if alt+left click, drag and annotate
+ if (NumCast(this.props.Document.scale, 1) !== 1) return;
+ if (!e.altKey && e.button === 0) {
+ PDFMenu.Instance.StartDrag = this.startDrag;
+ PDFMenu.Instance.Highlight = this.highlight;
+ PDFMenu.Instance.Snippet = this.createSnippet;
+ PDFMenu.Instance.Status = "pdf";
+ PDFMenu.Instance.fadeOut(true);
+ if (e.target && (e.target as any).parentElement.className === "textLayer") {
+ e.stopPropagation();
+ if (!e.ctrlKey) {
+ this.receiveAnnotations([], -1);
+ }
+ }
+ else {
+ // set marquee x and y positions to the spatially transformed position
+ if (this._mainCont.current) {
+ let boundingRect = this._mainCont.current.getBoundingClientRect();
+ this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width);
+ this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop;
+ }
+ this._marqueeing = true;
+ this._marquee.current && (this._marquee.current.style.opacity = "0.2");
+ this.receiveAnnotations([], -1);
+ }
+ document.removeEventListener("pointermove", this.onSelectStart);
+ document.addEventListener("pointermove", this.onSelectStart);
+ document.removeEventListener("pointerup", this.onSelectEnd);
+ document.addEventListener("pointerup", this.onSelectEnd);
+ }
+ }
@action
- toggleSearch = (e: React.MouseEvent) => {
- e && e.stopPropagation();
- this._searching = !this._searching;
-
- if (this._searching) {
- if (!this._pdfFindController && this._mainCont.current && this._viewer.current && !this._pdfViewer) {
- document.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = this.props.Document[WidthSym]() / this._pageSizes[0].width);
- document.addEventListener("pagerendered", () => console.log("rendered"));
- var pdfLinkService = new PDFJSViewer.PDFLinkService();
- this._pdfFindController = new PDFJSViewer.PDFFindController({
- linkService: pdfLinkService,
- });
- this._pdfViewer = new PDFJSViewer.PDFViewer({
- container: this._mainCont.current,
- viewer: this._viewer.current,
- linkService: pdfLinkService,
- findController: this._pdfFindController,
- renderer: "svg"
- });
- pdfLinkService.setViewer(this._pdfViewer);
- pdfLinkService.setDocument(this.props.pdf, null);
- this._pdfViewer.setDocument(this.props.pdf);
+ onSelectStart = (e: PointerEvent): void => {
+ if (this._marqueeing && this._mainCont.current) {
+ // transform positions and find the width and height to set the marquee to
+ let boundingRect = this._mainCont.current.getBoundingClientRect();
+ this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width)) - this._startX;
+ this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height)) - this._startY + this._mainCont.current.scrollTop;
+ 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);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ else if (e.target && (e.target as any).parentElement === this._mainCont.current) {
+ e.stopPropagation();
+ }
+ }
+
+ @action
+ createTextAnnotation = (sel: Selection, selRange: Range) => {
+ if (this._mainCont.current) {
+ let boundingRect = this._mainCont.current.getBoundingClientRect();
+ let clientRects = selRange.getClientRects();
+ for (let i = 0; i < clientRects.length; i++) {
+ let rect = clientRects.item(i);
+ if (rect/* && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height / this.props.pdf.numPages*/) {
+ let page = this.getPageFromScroll(rect.top);
+ let scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
+ let scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
+ if (rect.width !== this._mainCont.current.clientWidth) {
+ let annoBox = document.createElement("div");
+ annoBox.className = "pdfPage-annotationBox";
+ // transforms the positions from screen onto the pdf div
+ annoBox.style.top = ((rect.top - boundingRect.top) * scaleY + this._mainCont.current.scrollTop).toString();
+ annoBox.style.left = ((rect.left - boundingRect.left) * scaleX).toString();
+ annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width).toString();
+ annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height).toString();
+ this.createAnnotation(annoBox, this.getPageFromScroll(rect.top));
+ }
+ }
+ }
+ }
+ let text = selRange.cloneContents().textContent;
+ text && this.setSelectionText(text);
+
+ // clear selection
+ if (sel.empty) { // Chrome
+ sel.empty();
+ } else if (sel.removeAllRanges) { // Firefox
+ sel.removeAllRanges();
+ }
+ }
+
+ @action
+ onSelectEnd = (e: PointerEvent): void => {
+ if (this._marqueeing) {
+ this._marqueeing = false;
+ if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
+ if (this._marquee.current) { // make a copy of the marquee
+ let copy = document.createElement("div");
+ let style = this._marquee.current.style;
+ 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.className = "pdfPage-annotationBox";
+ this.createAnnotation(copy, this.getPageFromScroll(this._marqueeY));
+ this._marquee.current.style.opacity = "0";
+ }
+
+ if (!e.ctrlKey) {
+ PDFMenu.Instance.Status = "snippet";
+ PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight };
+ }
+ PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
}
+
+ this._marqueeHeight = this._marqueeWidth = 0;
+ }
+ else {
+ let sel = window.getSelection();
+ if (sel && sel.type === "Range") {
+ let selRange = sel.getRangeAt(0);
+ this.createTextAnnotation(sel, selRange);
+ PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
+ }
+ }
+
+ if (PDFMenu.Instance.Highlighting) {
+ this.highlight(undefined, "goldenrod");
+ }
+ else {
+ PDFMenu.Instance.StartDrag = this.startDrag;
+ PDFMenu.Instance.Highlight = this.highlight;
}
+ document.removeEventListener("pointermove", this.onSelectStart);
+ document.removeEventListener("pointerup", this.onSelectEnd);
+ }
+
+ @action
+ highlight = (targetDoc: Doc | undefined, color: string) => {
+ // creates annotation documents for current highlights
+ let annotationDoc = this.makeAnnotationDocument(targetDoc, color, false);
+ Doc.AddDocToList(this.props.fieldExtensionDoc, "annotations", 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 = (e: PointerEvent, ele: HTMLElement): void => {
+ e.preventDefault();
+ e.stopPropagation();
+ let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" });
+ targetDoc.targetPage = this.getPageFromScroll(this._marqueeY);
+ let annotationDoc = this.highlight(undefined, "red");
+ annotationDoc.linkedToDoc = false;
+ let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
+ DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
+ handlers: {
+ dragComplete: () => {
+ if (!annotationDoc.linkedToDoc) {
+ let annotations = DocListCast(annotationDoc.annotations);
+ annotations && annotations.forEach(anno => anno.target = targetDoc);
+ DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`);
+ }
+ }
+ },
+ hideSource: false
+ });
+ }
+
+ createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
+ let view = Doc.MakeAlias(this.props.Document);
+ let data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
+ data.title = StrCast(data.title) + "_snippet";
+ view.proto = data;
+ view.nativeHeight = marquee.height;
+ view.height = (this.props.Document[WidthSym]() / NumCast(this.props.Document.nativeWidth)) * marquee.height;
+ view.nativeWidth = this.props.Document.nativeWidth;
+ view.startY = marquee.top;
+ view.width = this.props.Document[WidthSym]();
+ DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0);
}
render() {
- trace();
let scaling = this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width / this.props.Document[WidthSym]() : 1;
- return (<div className="pdfViewer-viewer" ref={this._mainCont} >
- <div className="pdfViewer-text" key="viewerText" style={{ transform: `scale(${scaling})` }} >
+ return (<div className="pdfViewer-viewer" onPointerDown={this.onPointerDown} ref={this._mainCont}>
+ <div className="pdfViewer-text" style={{ transform: `scale(${scaling})` }}>
<div key="viewerReal" ref={this._viewer} />
</div>
+ <div className="pdfViewer-dragAnnotationBox" ref={this._marquee}
+ style={{
+ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`,
+ width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`,
+ border: `${this._marqueeWidth === 0 ? "" : "10px dashed black"}`
+ }}>
+ </div>
<div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.props.Document.nativeHeight) }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
<Annotation {...this.props} anno={anno} key={`${anno[Id]}-annotation`} />)}
</div>
- <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()}
- style={{ bottom: 0, left: `${this._searching ? 0 : 100}%` }}>
- <button className="pdfViewer-overlayButton" title="Open Search Bar" />
- <input className="pdfViewer-overlaySearchBar" placeholder="Search" onChange={this.searchStringChanged}
- onKeyDown={(e: React.KeyboardEvent) => e.keyCode === KeyCodes.ENTER ? this.search(this._searchString) : e.keyCode === KeyCodes.BACKSPACE ? e.stopPropagation() : true} />
- <button title="Search" onClick={() => this.search(this._searchString)}>
- <FontAwesomeIcon icon="search" size="3x" color="white" /></button>
- </div>
<button className="pdfViewer-overlayButton" onClick={this.prevAnnotation} title="Previous Annotation"
style={{ bottom: 280, right: 10, display: this.props.active() ? "flex" : "none" }}>
<div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
@@ -359,12 +539,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
<div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
<FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="3x" /></div>
</button>
- <button className="pdfViewer-overlayButton" onClick={this.toggleSearch} title="Open Search Bar"
- style={{ bottom: 10, right: 0, display: this.props.active() ? "flex" : "none" }}>
- <div className="pdfViewer-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div>
- <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
- <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="3x" /></div>
- </button>
</div >);
}
}
diff --git a/src/client/views/pdf/Page.scss b/src/client/views/pdf/Page.scss
deleted file mode 100644
index d8034b4b4..000000000
--- a/src/client/views/pdf/Page.scss
+++ /dev/null
@@ -1,36 +0,0 @@
-
-.pdfViewer-text {
- .page {
- position: relative;
- }
-}
-.pdfPage-cont {
- position: relative;
-
- .pdfPage-canvasContainer {
- position: absolute;
- }
-
- .pdfPage-dragAnnotationBox {
- position: absolute;
- background-color: transparent;
- opacity: 0.1;
- }
-
- .pdfPage-textLayer {
- position: absolute;
- width: 100%;
- height: 100%;
- div {
- user-select: text;
- }
- span {
- color: transparent;
- position: absolute;
- white-space: pre;
- cursor: text;
- -webkit-transform-origin: 0% 0%;
- transform-origin: 0% 0%;
- }
- }
-} \ No newline at end of file
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
deleted file mode 100644
index 533247170..000000000
--- a/src/client/views/pdf/Page.tsx
+++ /dev/null
@@ -1,293 +0,0 @@
-import { action, IReactionDisposer, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as Pdfjs from "pdfjs-dist";
-import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCastAsync, Opt, WidthSym } from "../../../new_fields/Doc";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DragManager } from "../../util/DragManager";
-import PDFMenu from "./PDFMenu";
-import { scale } from "./PDFViewer";
-import "./Page.scss";
-import React = require("react");
-
-
-interface IPageProps {
- size: { width: number, height: number };
- pdf: Pdfjs.PDFDocumentProxy;
- name: string;
- numPages: number;
- page: number;
- pageLoaded: (page: Pdfjs.PDFPageViewport) => void;
- fieldExtensionDoc: Doc;
- Document: Doc;
- renderAnnotations: (annotations: Doc[], removeOld: boolean) => void;
- sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
- createAnnotation: (div: HTMLDivElement, page: number) => void;
- makeAnnotationDocuments: (doc: Doc | undefined, color: string, linkTo: boolean) => Doc;
- getScrollFromPage: (page: number) => number;
- setSelectionText: (text: string) => void;
-}
-
-@observer
-export default class Page extends React.Component<IPageProps> {
- @observable private _state: "N/A" | "rendering" = "N/A";
- @observable private _width: number = this.props.size.width;
- @observable private _height: number = this.props.size.height;
- @observable private _page: Opt<Pdfjs.PDFPageProxy>;
- @observable private _currPage: number = this.props.page + 1;
- @observable private _marqueeX: number = 0;
- @observable private _marqueeY: number = 0;
- @observable private _marqueeWidth: number = 0;
- @observable private _marqueeHeight: number = 0;
-
- private _canvas: React.RefObject<HTMLCanvasElement> = React.createRef();
- private _textLayer: React.RefObject<HTMLDivElement> = React.createRef();
- private _marquee: React.RefObject<HTMLDivElement> = React.createRef();
- private _marqueeing: boolean = false;
- private _reactionDisposer?: IReactionDisposer;
- private _startY: number = 0;
- private _startX: number = 0;
-
- componentDidMount = (): void => this.loadPage(this.props.pdf);
-
- componentDidUpdate = (): void => this.loadPage(this.props.pdf);
-
- componentWillUnmount = (): void => this._reactionDisposer && this._reactionDisposer();
-
- loadPage = (pdf: Pdfjs.PDFDocumentProxy): void => {
- pdf.getPage(this._currPage).then(page => this.renderPage(page));
- }
-
- @action
- renderPage = (page: Pdfjs.PDFPageProxy): void => {
- // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes
- if (this._state !== "rendering" && !this._page && this._canvas.current && this._textLayer.current) {
- this._state = "rendering";
- let viewport = page.getViewport(scale);
- this._canvas.current.width = this._width = viewport.width;
- this._canvas.current.height = this._height = viewport.height;
- this.props.pageLoaded(viewport);
- let ctx = this._canvas.current.getContext("2d");
- if (ctx) {
- //@ts-ignore
- page.render({ canvasContext: ctx, viewport: viewport, enableWebGL: true }); // renders the page onto the canvas context
- page.getTextContent().then(res => // renders text onto the text container
- //@ts-ignore
- Pdfjs.renderTextLayer({
- textContent: res,
- container: this._textLayer.current,
- viewport: viewport
- }));
-
- this._page = page;
- }
- }
- }
-
- @action
- highlight = (targetDoc: Doc | undefined, color: string) => {
- // creates annotation documents for current highlights
- let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, color, false);
- Doc.AddDocToList(this.props.fieldExtensionDoc, "annotations", 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 = (e: PointerEvent, ele: HTMLElement): void => {
- e.preventDefault();
- e.stopPropagation();
- if (this._textLayer.current) {
- let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" });
- targetDoc.targetPage = this.props.page;
- let annotationDoc = this.highlight(undefined, "red");
- annotationDoc.linkedToDoc = false;
- let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc);
- DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, {
- handlers: {
- dragComplete: async () => {
- if (!BoolCast(annotationDoc.linkedToDoc)) {
- let annotations = await DocListCastAsync(annotationDoc.annotations);
- annotations && annotations.forEach(anno => anno.target = targetDoc);
- DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`);
- }
- }
- },
- hideSource: false
- });
- }
- }
-
- // cleans up events and boolean
- endDrag = (e: PointerEvent): void => {
- e.stopPropagation();
- }
-
- createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => {
- let view = Doc.MakeAlias(this.props.Document);
- let data = Doc.MakeDelegate(Doc.GetProto(this.props.Document));
- data.title = StrCast(data.title) + "_snippet";
- view.proto = data;
- view.nativeHeight = marquee.height;
- view.height = (this.props.Document[WidthSym]() / NumCast(this.props.Document.nativeWidth)) * marquee.height;
- view.nativeWidth = this.props.Document.nativeWidth;
- view.startY = marquee.top + this.props.getScrollFromPage(this.props.page);
- view.width = this.props.Document[WidthSym]();
- DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0);
- }
-
- @action
- onPointerDown = (e: React.PointerEvent): void => {
- // if alt+left click, drag and annotate
- if (NumCast(this.props.Document.scale, 1) !== 1) return;
- if (!e.altKey && e.button === 0) {
- PDFMenu.Instance.StartDrag = this.startDrag;
- PDFMenu.Instance.Highlight = this.highlight;
- PDFMenu.Instance.Snippet = this.createSnippet;
- PDFMenu.Instance.Status = "pdf";
- PDFMenu.Instance.fadeOut(true);
- if (e.target && (e.target as any).parentElement === this._textLayer.current) {
- e.stopPropagation();
- if (!e.ctrlKey) {
- this.props.sendAnnotations([], -1);
- }
- }
- else {
- // set marquee x and y positions to the spatially transformed position
- if (this._textLayer.current) {
- let boundingRect = this._textLayer.current.getBoundingClientRect();
- this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width);
- this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height);
- }
- this._marqueeing = true;
- this._marquee.current && (this._marquee.current.style.opacity = "0.2");
- this.props.sendAnnotations([], -1);
- }
- document.removeEventListener("pointermove", this.onSelectStart);
- document.addEventListener("pointermove", this.onSelectStart);
- document.removeEventListener("pointerup", this.onSelectEnd);
- document.addEventListener("pointerup", this.onSelectEnd);
- }
- }
-
- @action
- onSelectStart = (e: PointerEvent): void => {
- if (this._marqueeing && this._textLayer.current) {
- // transform positions and find the width and height to set the marquee to
- let boundingRect = this._textLayer.current.getBoundingClientRect();
- this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)) - this._startX;
- this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)) - 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);
- e.stopPropagation();
- e.preventDefault();
- }
- else if (e.target && (e.target as any).parentElement === this._textLayer.current) {
- e.stopPropagation();
- }
- }
-
- @action
- onSelectEnd = (e: PointerEvent): void => {
- if (this._marqueeing) {
- this._marqueeing = false;
- if (this._marqueeWidth > 10 || this._marqueeHeight > 10) {
- if (this._marquee.current) { // make a copy of the marquee
- let copy = document.createElement("div");
- let style = this._marquee.current.style;
- 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.className = "pdfPage-annotationBox";
- this.props.createAnnotation(copy, this.props.page);
- this._marquee.current.style.opacity = "0";
- }
-
- if (!e.ctrlKey) {
- PDFMenu.Instance.Status = "snippet";
- PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight };
- }
- PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
- }
-
- this._marqueeHeight = this._marqueeWidth = 0;
- }
- else {
- let sel = window.getSelection();
- if (sel && sel.type === "Range") {
- let selRange = sel.getRangeAt(0);
- this.createTextAnnotation(sel, selRange);
- PDFMenu.Instance.jumpTo(e.clientX, e.clientY);
- }
- }
-
- if (PDFMenu.Instance.Highlighting) {
- this.highlight(undefined, "goldenrod");
- }
- else {
- PDFMenu.Instance.StartDrag = this.startDrag;
- PDFMenu.Instance.Highlight = this.highlight;
- }
- document.removeEventListener("pointermove", this.onSelectStart);
- document.removeEventListener("pointerup", this.onSelectEnd);
- }
-
- @action
- createTextAnnotation = (sel: Selection, selRange: Range) => {
- if (this._textLayer.current) {
- let boundingRect = this._textLayer.current.getBoundingClientRect();
- let clientRects = selRange.getClientRects();
- for (let i = 0; i < clientRects.length; i++) {
- let rect = clientRects.item(i);
- if (rect && rect.width !== this._textLayer.current.getBoundingClientRect().width && rect.height !== this._textLayer.current.getBoundingClientRect().height) {
- let annoBox = document.createElement("div");
- annoBox.className = "pdfPage-annotationBox";
- // transforms the positions from screen onto the pdf div
- annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString();
- annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString();
- annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString();
- annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString();
- this.props.createAnnotation(annoBox, this.props.page);
- }
- }
- }
- let text = selRange.cloneContents().textContent;
- text && this.props.setSelectionText(text);
-
- // clear selection
- if (sel.empty) { // Chrome
- sel.empty();
- } else if (sel.removeAllRanges) { // Firefox
- sel.removeAllRanges();
- }
- }
-
- doubleClick = (e: React.MouseEvent) => {
- if (e.target && (e.target as any).parentElement === this._textLayer.current) {
- // do something to select the paragraph ideally
- }
- }
-
- render() {
- return (
- <div className={"pdfPage-cont"} onPointerDown={this.onPointerDown} onDoubleClick={this.doubleClick} style={{ "width": this._width, "height": this._height }}>
- <canvas className="PdfPage-canvasContainer" ref={this._canvas} />
- <div className="pdfPage-dragAnnotationBox" ref={this._marquee}
- style={{
- left: `${this._marqueeX}px`, top: `${this._marqueeY}px`,
- width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`,
- border: `${this._marqueeWidth === 0 ? "" : "10px dashed black"}`
- }}>
- </div>
- <div className="pdfPage-textlayer" ref={this._textLayer} />
- </div>);
- }
-} \ No newline at end of file
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 79f87f4ac..3baedce4b 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -2,39 +2,12 @@ import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { DocServer } from '../client/DocServer';
import { Doc } from '../new_fields/Doc';
+import * as Pdfjs from "pdfjs-dist";
+import "pdfjs-dist/web/pdf_viewer.css";
+import { Utils } from '../Utils';
+const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const protoId = "protoDoc";
const delegateId = "delegateDoc";
class Test extends React.Component {
- onCreateClick = () => {
- const proto = new Doc(protoId, true);
- const delegate = Doc.MakeDelegate(proto, delegateId);
- }
-
- onReadClick = async () => {
- console.log("reading");
- const docs = await DocServer.GetRefFields([delegateId, protoId]);
- console.log("done");
- console.log(docs);
- }
-
- onDeleteClick = () => {
- DocServer.DeleteDocuments([protoId, delegateId]);
- }
-
- render() {
- return (
- <div>
- <button onClick={this.onCreateClick}>Create Docs</button>
- <button onClick={this.onReadClick}>Read Docs</button>
- <button onClick={this.onDeleteClick}>Delete Docs</button>
- </div>
- );
- }
}
-
-DocServer.init(window.location.protocol, window.location.hostname, 4321, "test");
-ReactDOM.render(
- <Test />,
- document.getElementById('root')
-); \ No newline at end of file