aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-04-03 11:54:37 -0400
committerbob <bcz@cs.brown.edu>2019-04-03 11:54:37 -0400
commit6d327d692a316b7dacbb542950d913a9ca361dbb (patch)
tree11d32c084c2cf83794fc689564caeadae9c996c9
parentbb7d5a26ec68f283c5adb42d4d6554253de7176f (diff)
parentbc572b4459455e6b046972b0f984868acc6701b5 (diff)
Merge branch 'minimize' into decor
-rw-r--r--package.json1
-rw-r--r--src/client/util/DragManager.ts484
-rw-r--r--src/client/util/SelectionManager.ts82
-rw-r--r--src/client/views/DocumentDecorations.scss9
-rw-r--r--src/client/views/nodes/DocumentView.scss18
-rw-r--r--src/client/views/nodes/DocumentView.tsx687
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx289
-rw-r--r--src/client/views/nodes/Sticky.tsx138
-rw-r--r--src/fields/Document.ts712
-rw-r--r--src/fields/KeyStore.ts90
-rw-r--r--src/fields/MinimizedField.tsx29
-rw-r--r--src/server/Message.ts168
-rw-r--r--src/server/ServerUtil.ts150
13 files changed, 1620 insertions, 1237 deletions
diff --git a/package.json b/package.json
index 27b3eead1..6e894fd20 100644
--- a/package.json
+++ b/package.json
@@ -105,6 +105,7 @@
"express-validator": "^5.3.1",
"expressjs": "^1.0.1",
"flexlayout-react": "^0.3.3",
+ "font-awesome": "^4.7.0",
"formidable": "^1.2.1",
"golden-layout": "^1.5.9",
"html-to-image": "^0.1.0",
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9ffe964ef..c0f482e18 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,251 +1,311 @@
import { DocumentDecorations } from "../views/DocumentDecorations";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { Document } from "../../fields/Document"
+import { Document } from "../../fields/Document";
import { action } from "mobx";
import { ImageField } from "../../fields/ImageField";
import { KeyStore } from "../../fields/KeyStore";
import { CollectionView } from "../views/collections/CollectionView";
import { DocumentView } from "../views/nodes/DocumentView";
-export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) {
- let onRowMove = action((e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
+export function setupDrag(
+ _reference: React.RefObject<HTMLDivElement>,
+ docFunc: () => Document,
+ removeFunc: (containingCollection: CollectionView) => void = () => {}
+) {
+ let onRowMove = action(
+ (e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener('pointerup', onRowUp);
- var dragData = new DragManager.DocumentDragData([docFunc()]);
- dragData.removeDocument = removeFunc;
- DragManager.StartDocumentDrag([_reference.current!], dragData);
- });
- let onRowUp = action((e: PointerEvent): void => {
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener('pointerup', onRowUp);
- });
- let onItemDown = (e: React.PointerEvent) => {
- // if (this.props.isSelected() || this.props.isTopMost) {
- if (e.button == 0) {
- e.stopPropagation();
- if (e.shiftKey) {
- CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
- } else {
- document.addEventListener("pointermove", onRowMove);
- document.addEventListener('pointerup', onRowUp);
- }
- }
- //}
+ document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener("pointerup", onRowUp);
+ var dragData = new DragManager.DocumentDragData([docFunc()]);
+ dragData.removeDocument = removeFunc;
+ DragManager.StartDocumentDrag([_reference.current!], dragData);
+ }
+ );
+ let onRowUp = action(
+ (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener("pointerup", onRowUp);
+ }
+ );
+ let onItemDown = (e: React.PointerEvent) => {
+ // if (this.props.isSelected() || this.props.isTopMost) {
+ if (e.button == 0) {
+ e.stopPropagation();
+ if (e.shiftKey) {
+ CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
+ } else {
+ document.addEventListener("pointermove", onRowMove);
+ document.addEventListener("pointerup", onRowUp);
+ }
}
- return onItemDown;
+ //}
+ };
+ return onItemDown;
}
export namespace DragManager {
- export function Root() {
- const root = document.getElementById("root");
- if (!root) {
- throw new Error("No root element found");
- }
- return root;
+ export function Root() {
+ const root = document.getElementById("root");
+ if (!root) {
+ throw new Error("No root element found");
}
+ return root;
+ }
- let dragDiv: HTMLDivElement;
+ let dragDiv: HTMLDivElement;
- export enum DragButtons {
- Left = 1, Right = 2, Both = Left | Right
- }
+ export enum DragButtons {
+ Left = 1,
+ Right = 2,
+ Both = Left | Right
+ }
- interface DragOptions {
- handlers: DragHandlers;
+ interface DragOptions {
+ handlers: DragHandlers;
- hideSource: boolean | (() => boolean);
- }
+ hideSource: boolean | (() => boolean);
+ }
- export interface DragDropDisposer {
- (): void;
- }
+ export interface DragDropDisposer {
+ (): void;
+ }
- export class DragCompleteEvent {
- }
+ export class DragCompleteEvent {}
- export interface DragHandlers {
- dragComplete: (e: DragCompleteEvent) => void;
- }
+ export interface DragHandlers {
+ dragComplete: (e: DragCompleteEvent) => void;
+ }
- export interface DropOptions {
- handlers: DropHandlers;
- }
- export class DropEvent {
- constructor(readonly x: number, readonly y: number, readonly data: { [id: string]: any }) { }
- }
+ export interface DropOptions {
+ handlers: DropHandlers;
+ }
+ export class DropEvent {
+ constructor(
+ readonly x: number,
+ readonly y: number,
+ readonly data: { [id: string]: any }
+ ) {}
+ }
+ export interface DropHandlers {
+ drop: (e: Event, de: DropEvent) => void;
+ }
+ export function MakeDropTarget(
+ element: HTMLElement,
+ options: DropOptions
+ ): DragDropDisposer {
+ if ("canDrop" in element.dataset) {
+ throw new Error(
+ "Element is already droppable, can't make it droppable again"
+ );
+ }
+ element.dataset["canDrop"] = "true";
+ const handler = (e: Event) => {
+ const ce = e as CustomEvent<DropEvent>;
+ options.handlers.drop(e, ce.detail);
+ };
+ element.addEventListener("dashOnDrop", handler);
+ return () => {
+ element.removeEventListener("dashOnDrop", handler);
+ delete element.dataset["canDrop"];
+ };
+ }
- export interface DropHandlers {
- drop: (e: Event, de: DropEvent) => void;
+ export class DocumentDragData {
+ constructor(dragDoc: Document[]) {
+ this.draggedDocuments = dragDoc;
+ this.droppedDocuments = dragDoc;
}
+ draggedDocuments: Document[];
+ droppedDocuments: Document[];
+ xOffset?: number;
+ yOffset?: number;
+ aliasOnDrop?: boolean;
+ removeDocument?: (collectionDrop: CollectionView) => void;
+ [id: string]: any;
+ }
+ export function StartDocumentDrag(
+ eles: HTMLElement[],
+ dragData: DocumentDragData,
+ options?: DragOptions
+ ) {
+ StartDrag(
+ eles,
+ dragData,
+ options,
+ (dropData: { [id: string]: any }) =>
+ (dropData.droppedDocuments = dragData.aliasOnDrop
+ ? dragData.draggedDocuments.map(d => d.CreateAlias())
+ : dragData.draggedDocuments)
+ );
+ }
- export function MakeDropTarget(element: HTMLElement, options: DropOptions): DragDropDisposer {
- if ("canDrop" in element.dataset) {
- throw new Error("Element is already droppable, can't make it droppable again");
- }
- element.dataset["canDrop"] = "true";
- const handler = (e: Event) => {
- const ce = e as CustomEvent<DropEvent>;
- options.handlers.drop(e, ce.detail);
- };
- element.addEventListener("dashOnDrop", handler);
- return () => {
- element.removeEventListener("dashOnDrop", handler);
- delete element.dataset["canDrop"]
- };
+ export class LinkDragData {
+ constructor(linkSourceDoc: DocumentView) {
+ this.linkSourceDocumentView = linkSourceDoc;
}
-
- export class DocumentDragData {
- constructor(dragDoc: Document[]) {
- this.draggedDocuments = dragDoc;
- this.droppedDocuments = dragDoc;
- }
- draggedDocuments: Document[];
- droppedDocuments: Document[];
- xOffset?: number;
- yOffset?: number;
- aliasOnDrop?: boolean;
- removeDocument?: (collectionDrop: CollectionView) => void;
- [id: string]: any;
+ linkSourceDocumentView: DocumentView;
+ [id: string]: any;
+ }
+ export function StartLinkDrag(
+ ele: HTMLElement,
+ dragData: LinkDragData,
+ options?: DragOptions
+ ) {
+ StartDrag([ele], dragData, options);
+ }
+ function StartDrag(
+ eles: HTMLElement[],
+ dragData: { [id: string]: any },
+ options?: DragOptions,
+ finishDrag?: (dropData: { [id: string]: any }) => void
+ ) {
+ if (!dragDiv) {
+ dragDiv = document.createElement("div");
+ dragDiv.className = "dragManager-dragDiv";
+ DragManager.Root().appendChild(dragDiv);
}
- export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, options?: DragOptions) {
- StartDrag(eles, dragData, options, (dropData: { [id: string]: any }) => dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => d.CreateAlias()) : dragData.draggedDocuments);
- }
+ let scaleXs: number[] = [];
+ let scaleYs: number[] = [];
+ let xs: number[] = [];
+ let ys: number[] = [];
- export class LinkDragData {
- constructor(linkSourceDoc: DocumentView) {
- this.linkSourceDocumentView = linkSourceDoc;
- }
- linkSourceDocumentView: DocumentView;
- [id: string]: any;
- }
- export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, options?: DragOptions) {
- StartDrag([ele], dragData, options);
- }
- function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
- if (!dragDiv) {
- dragDiv = document.createElement("div");
- dragDiv.className = "dragManager-dragDiv"
- DragManager.Root().appendChild(dragDiv);
+ const docs: Document[] =
+ dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
+ let dragElements = eles.map(ele => {
+ const w = ele.offsetWidth,
+ h = ele.offsetHeight;
+ const rect = ele.getBoundingClientRect();
+ const scaleX = rect.width / w,
+ scaleY = rect.height / h;
+ let x = rect.left,
+ y = rect.top;
+ xs.push(x);
+ ys.push(y);
+ scaleXs.push(scaleX);
+ scaleYs.push(scaleY);
+ let dragElement = ele.cloneNode(true) as HTMLElement;
+ dragElement.style.opacity = "0.7";
+ dragElement.style.position = "absolute";
+ dragElement.style.bottom = "";
+ dragElement.style.left = "";
+ dragElement.style.transformOrigin = "0 0";
+ dragElement.style.zIndex = "1000";
+ dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
+ dragElement.style.width = `${rect.width / scaleX}px`;
+ dragElement.style.height = `${rect.height / scaleY}px`;
+
+ // bcz: PDFs don't show up if you clone them because they contain a canvas.
+ // however, PDF's have a thumbnail field that contains an image of their canvas.
+ // So we replace the pdf's canvas with the image thumbnail
+ if (docs.length) {
+ var pdfBox = dragElement.getElementsByClassName(
+ "pdfBox-cont"
+ )[0] as HTMLElement;
+ let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
+ if (pdfBox && pdfBox.childElementCount && thumbnail) {
+ let img = new Image();
+ img!.src = thumbnail.toString();
+ img!.style.position = "absolute";
+ img!.style.width = `${rect.width / scaleX}px`;
+ img!.style.height = `${rect.height / scaleY}px`;
+ pdfBox.replaceChild(img!, pdfBox.children[0]);
}
+ }
- let scaleXs: number[] = [];
- let scaleYs: number[] = [];
- let xs: number[] = [];
- let ys: number[] = [];
-
- const docs: Document[] = dragData instanceof DocumentDragData ? dragData.draggedDocuments : [];
- let dragElements = eles.map(ele => {
- const w = ele.offsetWidth, h = ele.offsetHeight;
- const rect = ele.getBoundingClientRect();
- const scaleX = rect.width / w, scaleY = rect.height / h;
- let x = rect.left, y = rect.top;
- xs.push(x); ys.push(y);
- scaleXs.push(scaleX); scaleYs.push(scaleY);
- let dragElement = ele.cloneNode(true) as HTMLElement;
- dragElement.style.opacity = "0.7";
- dragElement.style.position = "absolute";
- dragElement.style.bottom = "";
- dragElement.style.left = "";
- dragElement.style.transformOrigin = "0 0";
- dragElement.style.zIndex = "1000";
- dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`;
- dragElement.style.width = `${rect.width / scaleX}px`;
- dragElement.style.height = `${rect.height / scaleY}px`;
-
- // bcz: PDFs don't show up if you clone them because they contain a canvas.
- // however, PDF's have a thumbnail field that contains an image of their canvas.
- // So we replace the pdf's canvas with the image thumbnail
- if (docs.length) {
- var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement;
- let thumbnail = docs[0].GetT(KeyStore.Thumbnail, ImageField);
- if (pdfBox && pdfBox.childElementCount && thumbnail) {
- let img = new Image();
- img!.src = thumbnail.toString();
- img!.style.position = "absolute";
- img!.style.width = `${rect.width / scaleX}px`;
- img!.style.height = `${rect.height / scaleY}px`;
- pdfBox.replaceChild(img!, pdfBox.children[0])
- }
- }
-
- dragDiv.appendChild(dragElement);
- return dragElement;
- });
+ dragDiv.appendChild(dragElement);
+ return dragElement;
+ });
- let hideSource = false;
- if (options) {
- if (typeof options.hideSource === "boolean") {
- hideSource = options.hideSource;
- } else {
- hideSource = options.hideSource();
- }
- }
- eles.map(ele => ele.hidden = hideSource);
-
- const moveHandler = (e: PointerEvent) => {
- e.stopPropagation();
- e.preventDefault();
- if (dragData instanceof DocumentDragData)
- dragData.aliasOnDrop = e.ctrlKey || e.altKey;
- if (e.shiftKey) {
- abortDrag();
- CollectionDockingView.Instance.StartOtherDrag(docs, { pageX: e.pageX, pageY: e.pageY, preventDefault: () => { }, button: 0 });
- }
- dragElements.map((dragElement, i) => dragElement.style.transform = `translate(${xs[i] += e.movementX}px, ${ys[i] += e.movementY}px) scale(${scaleXs[i]}, ${scaleYs[i]})`);
- };
-
- const abortDrag = () => {
- document.removeEventListener("pointermove", moveHandler, true);
- document.removeEventListener("pointerup", upHandler);
- dragElements.map(dragElement => dragDiv.removeChild(dragElement));
- eles.map(ele => ele.hidden = false);
- }
- const upHandler = (e: PointerEvent) => {
- abortDrag();
- FinishDrag(eles, e, dragData, options, finishDrag);
- };
- document.addEventListener("pointermove", moveHandler, true);
- document.addEventListener("pointerup", upHandler);
+ let hideSource = false;
+ if (options) {
+ if (typeof options.hideSource === "boolean") {
+ hideSource = options.hideSource;
+ } else {
+ hideSource = options.hideSource();
+ }
}
+ eles.map(ele => (ele.hidden = hideSource));
- function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
- let removed = dragEles.map(dragEle => {
- let parent = dragEle.parentElement;
- if (parent)
- parent.removeChild(dragEle);
- return [dragEle, parent];
- });
- const target = document.elementFromPoint(e.x, e.y);
- removed.map(r => {
- let dragEle: HTMLElement = r[0]!;
- let parent: HTMLElement | null = r[1];
- if (parent)
- parent.appendChild(dragEle);
+ const moveHandler = (e: PointerEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+ if (dragData instanceof DocumentDragData)
+ dragData.aliasOnDrop = e.ctrlKey || e.altKey;
+ if (e.shiftKey) {
+ abortDrag();
+ CollectionDockingView.Instance.StartOtherDrag(docs, {
+ pageX: e.pageX,
+ pageY: e.pageY,
+ preventDefault: () => {},
+ button: 0
});
- if (target) {
- if (finishDrag)
- finishDrag(dragData);
-
- target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", {
- bubbles: true,
- detail: {
- x: e.x,
- y: e.y,
- data: dragData
- }
- }));
-
- if (options) {
- options.handlers.dragComplete({});
- }
- }
- DocumentDecorations.Instance.Hidden = false;
+ }
+ dragElements.map(
+ (dragElement, i) =>
+ (dragElement.style.transform = `translate(${(xs[i] +=
+ e.movementX)}px, ${(ys[i] += e.movementY)}px) scale(${
+ scaleXs[i]
+ }, ${scaleYs[i]})`)
+ );
+ };
+
+ const abortDrag = () => {
+ document.removeEventListener("pointermove", moveHandler, true);
+ document.removeEventListener("pointerup", upHandler);
+ dragElements.map(dragElement => dragDiv.removeChild(dragElement));
+ eles.map(ele => (ele.hidden = false));
+ };
+ const upHandler = (e: PointerEvent) => {
+ abortDrag();
+ FinishDrag(eles, e, dragData, options, finishDrag);
+ };
+ document.addEventListener("pointermove", moveHandler, true);
+ document.addEventListener("pointerup", upHandler);
+ }
+
+ function FinishDrag(
+ dragEles: HTMLElement[],
+ e: PointerEvent,
+ dragData: { [index: string]: any },
+ options?: DragOptions,
+ finishDrag?: (dragData: { [index: string]: any }) => void
+ ) {
+ let removed = dragEles.map(dragEle => {
+ let parent = dragEle.parentElement;
+ if (parent) parent.removeChild(dragEle);
+ return [dragEle, parent];
+ });
+ const target = document.elementFromPoint(e.x, e.y);
+ removed.map(r => {
+ let dragEle: HTMLElement = r[0]!;
+ let parent: HTMLElement | null = r[1];
+ if (parent) parent.appendChild(dragEle);
+ });
+ if (target) {
+ if (finishDrag) finishDrag(dragData);
+
+ target.dispatchEvent(
+ new CustomEvent<DropEvent>("dashOnDrop", {
+ bubbles: true,
+ detail: {
+ x: e.x,
+ y: e.y,
+ data: dragData
+ }
+ })
+ );
+
+ if (options) {
+ options.handlers.dragComplete({});
+ }
}
-} \ No newline at end of file
+ DocumentDecorations.Instance.Hidden = false;
+ }
+}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 1354e32e1..438659108 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,50 +1,50 @@
import { observable, action } from "mobx";
import { DocumentView } from "../views/nodes/DocumentView";
-import { Document } from "../../fields/Document"
+import { Document } from "../../fields/Document";
export namespace SelectionManager {
- class Manager {
- @observable
- SelectedDocuments: Array<DocumentView> = [];
-
- @action
- SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
- // if doc is not in SelectedDocuments, add it
- if (!ctrlPressed) {
- manager.SelectedDocuments = [];
- }
-
- if (manager.SelectedDocuments.indexOf(doc) === -1) {
- manager.SelectedDocuments.push(doc)
- }
- }
+ class Manager {
+ @observable
+ SelectedDocuments: Array<DocumentView> = [];
+
+ @action
+ SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
+ // if doc is not in SelectedDocuments, add it
+ if (!ctrlPressed) {
+ manager.SelectedDocuments = [];
+ }
+
+ if (manager.SelectedDocuments.indexOf(doc) === -1) {
+ manager.SelectedDocuments.push(doc);
+ }
}
+ }
- const manager = new Manager;
+ const manager = new Manager();
- export function SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
- manager.SelectDoc(doc, ctrlPressed)
+ export function SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
+ if (!doc.isMinimized()) {
+ manager.SelectDoc(doc, ctrlPressed);
}
-
- export function IsSelected(doc: DocumentView): boolean {
- return manager.SelectedDocuments.indexOf(doc) !== -1;
- }
-
- export function DeselectAll(except?: Document): void {
- let found: DocumentView | undefined = undefined;
- if (except) {
- for (let i = 0; i < manager.SelectedDocuments.length; i++) {
- let view = manager.SelectedDocuments[i];
- if (view.props.Document == except)
- found = view;
- }
- }
- manager.SelectedDocuments.length = 0;
- if (found)
- manager.SelectedDocuments.push(found);
- }
-
- export function SelectedDocuments(): Array<DocumentView> {
- return manager.SelectedDocuments;
+ }
+
+ export function IsSelected(doc: DocumentView): boolean {
+ return manager.SelectedDocuments.indexOf(doc) !== -1;
+ }
+
+ export function DeselectAll(except?: Document): void {
+ let found: DocumentView | undefined = undefined;
+ if (except) {
+ for (let i = 0; i < manager.SelectedDocuments.length; i++) {
+ let view = manager.SelectedDocuments[i];
+ if (view.props.Document == except) found = view;
+ }
}
-} \ No newline at end of file
+ manager.SelectedDocuments.length = 0;
+ if (found) manager.SelectedDocuments.push(found);
+ }
+
+ export function SelectedDocuments(): Array<DocumentView> {
+ return manager.SelectedDocuments;
+ }
+}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 272ea9e5d..c72623546 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,4 +1,5 @@
@import "global_variables";
+
#documentDecorations-container {
position: absolute;
display: grid;
@@ -6,26 +7,32 @@
grid-template-rows: 20px 8px 1fr 8px;
grid-template-columns: 8px 1fr 8px;
pointer-events: none;
+
#documentDecorations-centerCont {
background: none;
}
+
.documentDecorations-resizer {
pointer-events: auto;
background: $alt-accent;
opacity: 0.8;
}
+
#documentDecorations-topLeftResizer,
#documentDecorations-bottomRightResizer {
cursor: nwse-resize;
}
+
#documentDecorations-topRightResizer,
#documentDecorations-bottomLeftResizer {
cursor: nesw-resize;
}
+
#documentDecorations-topResizer,
#documentDecorations-bottomResizer {
cursor: ns-resize;
}
+
#documentDecorations-leftResizer,
#documentDecorations-rightResizer {
cursor: ew-resize;
@@ -39,7 +46,7 @@
}
.documentDecorations-background {
- background:lightblue;
+ background: lightblue;
position: absolute;
opacity: 0.1;
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 85a115f1c..127a6b535 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -1,23 +1,41 @@
@import "../global_variables";
+
.documentView-node {
position: absolute;
background: $light-color; //overflow: hidden;
+
&.minimized {
width: 30px;
height: 30px;
}
+
.top {
background: #232323;
height: 20px;
cursor: pointer;
}
+
.content {
padding: 20px 20px;
height: auto;
box-sizing: border-box;
}
+
.scroll-box {
overflow-y: scroll;
height: calc(100% - 20px);
}
}
+
+.minimized-box {
+ height: 10px;
+ width: 10px;
+ border-radius: 2px;
+ background: $dark-color
+}
+
+.minimized-box:hover {
+ background: $main-accent;
+ transform: scale(1.15);
+ cursor: pointer;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1195128dc..2b9372e15 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,12 @@
-import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
+import {
+ action,
+ computed,
+ IReactionDisposer,
+ reaction,
+ runInAction,
+ observable
+} from "mobx";
+import { library } from "@fortawesome/fontawesome-svg-core";
import { observer } from "mobx-react";
import { Document } from "../../../fields/Document";
import { Field, Opt, FieldWaiting } from "../../../fields/Field";
@@ -13,30 +21,34 @@ import { DragManager } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionView, CollectionViewType } from "../collections/CollectionView";
+import {
+ CollectionView,
+ CollectionViewType
+} from "../collections/CollectionView";
import { ContextMenu } from "../ContextMenu";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
import { ServerUtils } from "../../../server/ServerUtil";
-
+import { DocumentDecorations } from "../DocumentDecorations";
+import { MinimizedField } from "../../../fields/MinimizedField";
export interface DocumentViewProps {
- ContainingCollectionView: Opt<CollectionView>;
- Document: Document;
- AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean;
- RemoveDocument?: (doc: Document) => boolean;
- ScreenToLocalTransform: () => Transform;
- isTopMost: boolean;
- ContentScaling: () => number;
- PanelWidth: () => number;
- PanelHeight: () => number;
- focus: (doc: Document) => void;
- SelectOnLoad: boolean;
+ ContainingCollectionView: Opt<CollectionView>;
+ Document: Document;
+ AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean;
+ RemoveDocument?: (doc: Document) => boolean;
+ ScreenToLocalTransform: () => Transform;
+ isTopMost: boolean;
+ ContentScaling: () => number;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
+ focus: (doc: Document) => void;
+ SelectOnLoad: boolean;
}
export interface JsxArgs extends DocumentViewProps {
- Keys: { [name: string]: Key }
- Fields: { [name: string]: Field }
+ Keys: { [name: string]: Key };
+ Fields: { [name: string]: Field };
}
/*
@@ -55,287 +67,448 @@ Example usage of this function:
}
*/
export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
- let Keys: { [name: string]: any } = {}
- let Fields: { [name: string]: any } = {}
- for (const key of keys) {
- let fn = () => { }
- Object.defineProperty(fn, "name", { value: key + "Key" })
- Keys[key] = fn;
- }
- for (const field of fields) {
- let fn = () => { }
- Object.defineProperty(fn, "name", { value: field })
- Fields[field] = fn;
- }
- let args: JsxArgs = {
- Document: function Document() { },
- DocumentView: function DocumentView() { },
- Keys,
- Fields
- } as any;
- return args;
+ let Keys: { [name: string]: any } = {};
+ let Fields: { [name: string]: any } = {};
+ for (const key of keys) {
+ let fn = () => {};
+ Object.defineProperty(fn, "name", { value: key + "Key" });
+ Keys[key] = fn;
+ }
+ for (const field of fields) {
+ let fn = () => {};
+ Object.defineProperty(fn, "name", { value: field });
+ Fields[field] = fn;
+ }
+ let args: JsxArgs = {
+ Document: function Document() {},
+ DocumentView: function DocumentView() {},
+ Keys,
+ Fields
+ } as any;
+ return args;
}
export interface JsxBindings {
- Document: Document;
- isSelected: () => boolean;
- select: (isCtrlPressed: boolean) => void;
- isTopMost: boolean;
- SelectOnLoad: boolean;
- [prop: string]: any;
+ Document: Document;
+ isSelected: () => boolean;
+ select: (isCtrlPressed: boolean) => void;
+ isTopMost: boolean;
+ SelectOnLoad: boolean;
+ [prop: string]: any;
}
-
-
@observer
export class DocumentView extends React.Component<DocumentViewProps> {
- private _mainCont = React.createRef<HTMLDivElement>();
- private _downX: number = 0;
- private _downY: number = 0;
- private _reactionDisposer: Opt<IReactionDisposer>;
- @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); }
- @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; }
- @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
- @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
- @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
- screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
- onPointerDown = (e: React.PointerEvent): void => {
- this._downX = e.clientX;
- this._downY = e.clientY;
- if (e.shiftKey && e.buttons === 2) {
- if (this.props.isTopMost) {
- this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
- }
- else CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
- e.stopPropagation();
- } else {
- if (this.active && !e.isDefaultPrevented()) {
- e.stopPropagation();
- document.removeEventListener("pointermove", this.onPointerMove)
- document.addEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp)
- document.addEventListener("pointerup", this.onPointerUp);
- }
- }
+ private _mainCont = React.createRef<HTMLDivElement>();
+ private _downX: number = 0;
+ private _downY: number = 0;
+
+ private _reactionDisposer: Opt<IReactionDisposer>;
+ @computed get active(): boolean {
+ return (
+ SelectionManager.IsSelected(this) ||
+ !this.props.ContainingCollectionView ||
+ this.props.ContainingCollectionView.active()
+ );
+ }
+ @computed get topMost(): boolean {
+ return (
+ !this.props.ContainingCollectionView ||
+ this.props.ContainingCollectionView.collectionViewType ==
+ CollectionViewType.Docking
+ );
+ }
+ @computed get layout(): string {
+ return this.props.Document.GetText(
+ KeyStore.Layout,
+ "<p>Error loading layout data</p>"
+ );
+ }
+ @computed get layoutKeys(): Key[] {
+ return this.props.Document.GetData(
+ KeyStore.LayoutKeys,
+ ListField,
+ new Array<Key>()
+ );
+ }
+ @computed get layoutFields(): Key[] {
+ return this.props.Document.GetData(
+ KeyStore.LayoutFields,
+ ListField,
+ new Array<Key>()
+ );
+ }
+ screenRect = (): ClientRect | DOMRect =>
+ this._mainCont.current
+ ? this._mainCont.current.getBoundingClientRect()
+ : new DOMRect();
+ onPointerDown = (e: React.PointerEvent): void => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ if (e.shiftKey && e.buttons === 2) {
+ if (this.props.isTopMost) {
+ this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
+ } else
+ CollectionDockingView.Instance.StartOtherDrag([this.props.Document], e);
+ e.stopPropagation();
+ } else {
+ if (this.active && !e.isDefaultPrevented()) {
+ e.stopPropagation();
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.addEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointerup", this.onPointerUp);
+ }
}
+ };
- private dropDisposer?: DragManager.DragDropDisposer;
+ private dropDisposer?: DragManager.DragDropDisposer;
- componentDidMount() {
- if (this._mainCont.current) {
- this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
- }
- runInAction(() => DocumentManager.Instance.DocumentViews.push(this))
- this._reactionDisposer = reaction(
- () => this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.slice(),
- () => {
- if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.indexOf(this.props.Document.Id) != -1)
- SelectionManager.SelectDoc(this, true);
- });
+ componentDidMount() {
+ if (this._mainCont.current) {
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
+ handlers: { drop: this.drop.bind(this) }
+ });
}
+ runInAction(() => DocumentManager.Instance.DocumentViews.push(this));
+ this._reactionDisposer = reaction(
+ () =>
+ this.props.ContainingCollectionView &&
+ this.props.ContainingCollectionView.SelectedDocs.slice(),
+ () => {
+ if (
+ this.props.ContainingCollectionView &&
+ this.props.ContainingCollectionView.SelectedDocs.indexOf(
+ this.props.Document.Id
+ ) != -1
+ )
+ SelectionManager.SelectDoc(this, true);
+ }
+ );
+ }
- componentDidUpdate() {
- if (this.dropDisposer) {
- this.dropDisposer();
- }
- if (this._mainCont.current) {
- this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } });
- }
+ componentDidUpdate() {
+ if (this.dropDisposer) {
+ this.dropDisposer();
}
+ if (this._mainCont.current) {
+ this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, {
+ handlers: { drop: this.drop.bind(this) }
+ });
+ }
+ }
- componentWillUnmount() {
- if (this.dropDisposer) {
- this.dropDisposer();
- }
- runInAction(() => DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1))
- if (this._reactionDisposer) {
- this._reactionDisposer();
- }
+ componentWillUnmount() {
+ if (this.dropDisposer) {
+ this.dropDisposer();
}
+ runInAction(() =>
+ DocumentManager.Instance.DocumentViews.splice(
+ DocumentManager.Instance.DocumentViews.indexOf(this),
+ 1
+ )
+ );
+ if (this._reactionDisposer) {
+ this._reactionDisposer();
+ }
+ }
- startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
- if (this._mainCont.current) {
- const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- let dragData = new DragManager.DocumentDragData([this.props.Document]);
- dragData.aliasOnDrop = dropAliasOfDraggedDoc;
- dragData.xOffset = x - left;
- dragData.yOffset = y - top;
- dragData.removeDocument = (dropCollectionView: CollectionView) => {
- if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) {
- this.props.RemoveDocument(this.props.Document);
- }
- }
- DragManager.StartDocumentDrag([this._mainCont.current], dragData, {
- handlers: {
- dragComplete: action(() => { }),
- },
- hideSource: !dropAliasOfDraggedDoc
- })
+ startDragging(x: number, y: number, dropAliasOfDraggedDoc: boolean) {
+ if (this._mainCont.current) {
+ const [left, top] = this.props
+ .ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(0, 0);
+ let dragData = new DragManager.DocumentDragData([this.props.Document]);
+ dragData.aliasOnDrop = dropAliasOfDraggedDoc;
+ dragData.xOffset = x - left;
+ dragData.yOffset = y - top;
+ dragData.removeDocument = (dropCollectionView: CollectionView) => {
+ if (
+ this.props.RemoveDocument &&
+ this.props.ContainingCollectionView !== dropCollectionView
+ ) {
+ this.props.RemoveDocument(this.props.Document);
}
+ };
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, {
+ handlers: {
+ dragComplete: action(() => {})
+ },
+ hideSource: !dropAliasOfDraggedDoc
+ });
}
+ }
- onPointerMove = (e: PointerEvent): void => {
- if (e.cancelBubble) {
- return;
- }
- if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
- document.removeEventListener("pointermove", this.onPointerMove)
- document.removeEventListener("pointerup", this.onPointerUp);
- if (!this.topMost || e.buttons == 2 || e.altKey) {
- this.startDragging(e.x, e.y, e.ctrlKey || e.altKey);
- }
- }
- e.stopPropagation();
- e.preventDefault();
+ onPointerMove = (e: PointerEvent): void => {
+ if (e.cancelBubble) {
+ return;
}
- onPointerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onPointerMove)
- document.removeEventListener("pointerup", this.onPointerUp)
- e.stopPropagation();
- if (Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4) {
- SelectionManager.SelectDoc(this, e.ctrlKey);
- }
+ if (
+ Math.abs(this._downX - e.clientX) > 3 ||
+ Math.abs(this._downY - e.clientY) > 3
+ ) {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ if (!this.topMost || e.buttons == 2 || e.altKey) {
+ this.startDragging(e.x, e.y, e.ctrlKey || e.altKey);
+ }
}
- stopPropogation = (e: React.SyntheticEvent) => {
- e.stopPropagation();
+ e.stopPropagation();
+ e.preventDefault();
+ };
+ onPointerUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ e.stopPropagation();
+ if (
+ Math.abs(e.clientX - this._downX) < 4 &&
+ Math.abs(e.clientY - this._downY) < 4
+ ) {
+ SelectionManager.SelectDoc(this, e.ctrlKey);
}
+ };
+ stopPropogation = (e: React.SyntheticEvent) => {
+ e.stopPropagation();
+ };
- deleteClicked = (): void => {
- if (this.props.RemoveDocument) {
- this.props.RemoveDocument(this.props.Document);
- }
+ deleteClicked = (): void => {
+ if (this.props.RemoveDocument) {
+ this.props.RemoveDocument(this.props.Document);
}
+ };
- fieldsClicked = (e: React.MouseEvent): void => {
- if (this.props.AddDocument) {
- this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false);
- }
- }
- fullScreenClicked = (e: React.MouseEvent): void => {
- CollectionDockingView.Instance.OpenFullScreen(this.props.Document);
- ContextMenu.Instance.clearItems();
- ContextMenu.Instance.addItem({ description: "Close Full Screen", event: this.closeFullScreenClicked });
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
+ fieldsClicked = (e: React.MouseEvent): void => {
+ if (this.props.AddDocument) {
+ this.props.AddDocument(
+ Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }),
+ false
+ );
}
+ };
+ fullScreenClicked = (e: React.MouseEvent): void => {
+ CollectionDockingView.Instance.OpenFullScreen(this.props.Document);
+ ContextMenu.Instance.clearItems();
+ ContextMenu.Instance.addItem({
+ description: "Close Full Screen",
+ event: this.closeFullScreenClicked
+ });
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ };
- closeFullScreenClicked = (e: React.MouseEvent): void => {
- CollectionDockingView.Instance.CloseFullScreen();
- ContextMenu.Instance.clearItems();
- ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
- }
+ closeFullScreenClicked = (e: React.MouseEvent): void => {
+ CollectionDockingView.Instance.CloseFullScreen();
+ ContextMenu.Instance.clearItems();
+ ContextMenu.Instance.addItem({
+ description: "Full Screen",
+ event: this.fullScreenClicked
+ });
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ };
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.LinkDragData) {
- let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
- let destDoc: Document = this.props.Document;
- if (this.props.isTopMost) {
- return;
- }
- let linkDoc: Document = new Document();
+ @action
+ minimize = (e: React.MouseEvent): void => {
+ this.props.Document.SetData(
+ KeyStore.Minimized,
+ true as boolean,
+ MinimizedField
+ );
+ SelectionManager.DeselectAll();
+ };
- destDoc.GetTAsync(KeyStore.Prototype, Document).then((protoDest) =>
- sourceDoc.GetTAsync(KeyStore.Prototype, Document).then((protoSrc) => runInAction(() => {
- linkDoc.Set(KeyStore.Title, new TextField("New Link"));
- linkDoc.Set(KeyStore.LinkDescription, new TextField(""));
- linkDoc.Set(KeyStore.LinkTags, new TextField("Default"));
+ @action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ if (de.data instanceof DragManager.LinkDragData) {
+ let sourceDoc: Document = de.data.linkSourceDocumentView.props.Document;
+ let destDoc: Document = this.props.Document;
+ if (this.props.isTopMost) {
+ return;
+ }
+ let linkDoc: Document = new Document();
- let dstTarg = (protoDest ? protoDest : destDoc);
- let srcTarg = (protoSrc ? protoSrc : sourceDoc);
- linkDoc.Set(KeyStore.LinkedToDocs, dstTarg);
- linkDoc.Set(KeyStore.LinkedFromDocs, srcTarg);
- dstTarg.GetOrCreateAsync(KeyStore.LinkedFromDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) })
- srcTarg.GetOrCreateAsync(KeyStore.LinkedToDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) })
- }))
- )
- e.stopPropagation();
- }
- }
+ destDoc.GetTAsync(KeyStore.Prototype, Document).then(protoDest =>
+ sourceDoc.GetTAsync(KeyStore.Prototype, Document).then(protoSrc =>
+ runInAction(() => {
+ linkDoc.Set(KeyStore.Title, new TextField("New Link"));
+ linkDoc.Set(KeyStore.LinkDescription, new TextField(""));
+ linkDoc.Set(KeyStore.LinkTags, new TextField("Default"));
- onDrop = (e: React.DragEvent) => {
- if (e.isDefaultPrevented()) {
- return;
- }
- let text = e.dataTransfer.getData("text/plain");
- if (text && text.startsWith("<div")) {
- let oldLayout = this.props.Document.GetText(KeyStore.Layout, "");
- let layout = text.replace("{layout}", oldLayout);
- this.props.Document.SetText(KeyStore.Layout, layout);
- e.stopPropagation();
- e.preventDefault();
- }
+ let dstTarg = protoDest ? protoDest : destDoc;
+ let srcTarg = protoSrc ? protoSrc : sourceDoc;
+ linkDoc.Set(KeyStore.LinkedToDocs, dstTarg);
+ linkDoc.Set(KeyStore.LinkedFromDocs, srcTarg);
+ dstTarg.GetOrCreateAsync(
+ KeyStore.LinkedFromDocs,
+ ListField,
+ field => {
+ (field as ListField<Document>).Data.push(linkDoc);
+ }
+ );
+ srcTarg.GetOrCreateAsync(
+ KeyStore.LinkedToDocs,
+ ListField,
+ field => {
+ (field as ListField<Document>).Data.push(linkDoc);
+ }
+ );
+ })
+ )
+ );
+ e.stopPropagation();
}
+ };
- @action
- onContextMenu = (e: React.MouseEvent): void => {
- e.stopPropagation();
- let moved = Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3;
- if (moved || e.isDefaultPrevented()) {
- e.preventDefault()
- return;
- }
- e.preventDefault()
+ onDrop = (e: React.DragEvent) => {
+ if (e.isDefaultPrevented()) {
+ return;
+ }
+ let text = e.dataTransfer.getData("text/plain");
+ if (text && text.startsWith("<div")) {
+ let oldLayout = this.props.Document.GetText(KeyStore.Layout, "");
+ let layout = text.replace("{layout}", oldLayout);
+ this.props.Document.SetText(KeyStore.Layout, layout);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ };
- ContextMenu.Instance.addItem({ description: "Full Screen", event: this.fullScreenClicked })
- ContextMenu.Instance.addItem({ description: "Fields", event: this.fieldsClicked })
- ContextMenu.Instance.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) })
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) })
- ContextMenu.Instance.addItem({
- description: "Copy URL",
- event: () => {
- Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id));
- }
- });
- ContextMenu.Instance.addItem({
- description: "Copy ID",
- event: () => {
- Utils.CopyText(this.props.Document.Id);
- }
- });
- //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
- if (!this.topMost) {
- // DocumentViews should stop propagation of this event
- e.stopPropagation();
- }
+ @action
+ onContextMenu = (e: React.MouseEvent): void => {
+ e.stopPropagation();
+ let moved =
+ Math.abs(this._downX - e.clientX) > 3 ||
+ Math.abs(this._downY - e.clientY) > 3;
+ if (moved || e.isDefaultPrevented()) {
+ e.preventDefault();
+ return;
+ }
+ e.preventDefault();
- ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked })
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15)
- SelectionManager.SelectDoc(this, e.ctrlKey);
+ if (!this.isMinimized()) {
+ ContextMenu.Instance.addItem({
+ description: "Minimize",
+ event: this.minimize
+ });
+ }
+ ContextMenu.Instance.addItem({
+ description: "Full Screen",
+ event: this.fullScreenClicked
+ });
+ ContextMenu.Instance.addItem({
+ description: "Fields",
+ event: this.fieldsClicked
+ });
+ ContextMenu.Instance.addItem({
+ description: "Center",
+ event: () => this.props.focus(this.props.Document)
+ });
+ ContextMenu.Instance.addItem({
+ description: "Open Right",
+ event: () =>
+ CollectionDockingView.Instance.AddRightSplit(this.props.Document)
+ });
+ ContextMenu.Instance.addItem({
+ description: "Copy URL",
+ event: () => {
+ Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id));
+ }
+ });
+ ContextMenu.Instance.addItem({
+ description: "Copy ID",
+ event: () => {
+ Utils.CopyText(this.props.Document.Id);
+ }
+ });
+ //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) })
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ if (!this.topMost) {
+ // DocumentViews should stop propagation of this event
+ e.stopPropagation();
}
+ ContextMenu.Instance.addItem({
+ description: "Delete",
+ event: this.deleteClicked
+ });
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ SelectionManager.SelectDoc(this, e.ctrlKey);
+ };
- isSelected = () => {
- return SelectionManager.IsSelected(this);
+ isMinimized = () => {
+ let field = this.props.Document.GetT(KeyStore.Minimized, MinimizedField);
+ if (field && field !== FieldWaiting) {
+ return field.Data;
}
+ };
+
+ @action
+ expand = () => {
+ this.props.Document.SetData(
+ KeyStore.Minimized,
+ false as boolean,
+ MinimizedField
+ );
+ };
+
+ isSelected = () => {
+ return SelectionManager.IsSelected(this);
+ };
- select = (ctrlPressed: boolean) => {
- SelectionManager.SelectDoc(this, ctrlPressed)
+ select = (ctrlPressed: boolean) => {
+ SelectionManager.SelectDoc(this, ctrlPressed);
+ };
+
+ render() {
+ if (!this.props.Document) {
+ return null;
}
- render() {
- if (!this.props.Document) {
- return (null);
- }
- var scaling = this.props.ContentScaling();
- var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
- var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
- var backgroundcolor = this.props.Document.GetText(KeyStore.BackgroundColor, "");
- return (
- <div className="documentView-node" ref={this._mainCont}
- style={{
- background: backgroundcolor,
- width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%",
- height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%",
- transformOrigin: "left top",
- transform: `scale(${scaling} , ${scaling})`
- }}
- onDrop={this.onDrop}
- onContextMenu={this.onContextMenu}
- onPointerDown={this.onPointerDown} >
- <DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} layoutKey={KeyStore.Layout} />
- </div >
- )
+ var scaling = this.props.ContentScaling();
+ var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
+ var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+
+ if (this.isMinimized()) {
+ return (
+ <div
+ className="minimized-box"
+ ref={this._mainCont}
+ style={{
+ transformOrigin: "left top",
+ transform: `scale(${scaling} , ${scaling})`
+ }}
+ onClick={this.expand}
+ onDrop={this.onDrop}
+ onPointerDown={this.onPointerDown}
+ />
+ );
+ } else {
+ var backgroundcolor = this.props.Document.GetText(
+ KeyStore.BackgroundColor,
+ ""
+ );
+ return (
+ <div
+ className="documentView-node"
+ ref={this._mainCont}
+ style={{
+ background: backgroundcolor,
+ width: nativeWidth > 0 ? nativeWidth.toString() + "px" : "100%",
+ height: nativeHeight > 0 ? nativeHeight.toString() + "px" : "100%",
+ transformOrigin: "left top",
+ transform: `scale(${scaling} , ${scaling})`
+ }}
+ onDrop={this.onDrop}
+ onContextMenu={this.onContextMenu}
+ onPointerDown={this.onPointerDown}
+ >
+ <DocumentContentsView
+ {...this.props}
+ isSelected={this.isSelected}
+ select={this.select}
+ layoutKey={KeyStore.Layout}
+ />
+ </div>
+ );
}
-} \ No newline at end of file
+ }
+}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 512ad7d70..0c0efc437 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -3,28 +3,25 @@ import { baseKeymap } from "prosemirror-commands";
import { history, redo, undo } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
import { schema } from "../../util/RichTextSchema";
-import { EditorState, Transaction, } from "prosemirror-state";
+import { EditorState, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Opt, FieldWaiting } from "../../../fields/Field";
import "./FormattedTextBox.scss";
-import React = require("react")
+import React = require("react");
import { RichTextField } from "../../../fields/RichTextField";
import { FieldViewProps, FieldView } from "./FieldView";
-import { Plugin } from 'prosemirror-state'
-import { Decoration, DecorationSet } from 'prosemirror-view'
-import { TooltipTextMenu } from "../../util/TooltipTextMenu"
+import { Plugin } from "prosemirror-state";
+import { Decoration, DecorationSet } from "prosemirror-view";
+import { TooltipTextMenu } from "../../util/TooltipTextMenu";
import { ContextMenu } from "../../views/ContextMenu";
import { inpRules } from "../../util/RichTextRules";
const { buildMenuItems } = require("prosemirror-example-setup");
const { menuBar } = require("prosemirror-menu");
-
-
-
// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
//
// HTML Markup: <FormattedTextBox Doc={Document's ID} FieldKey={Key's name + "Key"}
-//
+//
// In Code, the node's HTML is specified in the document's parameterized structure as:
// document.SetField(KeyStore.Layout, "<FormattedTextBox doc={doc} fieldKey={<KEYNAME>Key} />");
// and the node's binding to the specified document KEYNAME as:
@@ -33,145 +30,161 @@ const { menuBar } = require("prosemirror-menu");
// 'fieldKey' property to the Key stored in LayoutKeys
// and 'doc' property to the document that is being rendered
//
-// When rendered() by React, this extracts the TextController from the Document stored at the
-// specified Key and assigns it to an HTML input node. When changes are made to this node,
+// When rendered() by React, this extracts the TextController from the Document stored at the
+// specified Key and assigns it to an HTML input node. When changes are made to this node,
// this will edit the document and assign the new value to that field.
//]
export class FormattedTextBox extends React.Component<FieldViewProps> {
-
- public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(FormattedTextBox, fieldStr) }
- private _ref: React.RefObject<HTMLDivElement>;
- private _editorView: Opt<EditorView>;
- private _reactionDisposer: Opt<IReactionDisposer>;
-
- constructor(props: FieldViewProps) {
- super(props);
-
- this._ref = React.createRef();
- this.onChange = this.onChange.bind(this);
+ public static LayoutString(fieldStr: string = "DataKey") {
+ return FieldView.LayoutString(FormattedTextBox, fieldStr);
+ }
+ private _ref: React.RefObject<HTMLDivElement>;
+ private _editorView: Opt<EditorView>;
+ private _reactionDisposer: Opt<IReactionDisposer>;
+
+ constructor(props: FieldViewProps) {
+ super(props);
+
+ this._ref = React.createRef();
+ this.onChange = this.onChange.bind(this);
+ }
+
+ dispatchTransaction = (tx: Transaction) => {
+ if (this._editorView) {
+ const state = this._editorView.state.apply(tx);
+ this._editorView.updateState(state);
+ const { doc, fieldKey } = this.props;
+ doc.SetDataOnPrototype(
+ fieldKey,
+ JSON.stringify(state.toJSON()),
+ RichTextField
+ );
+ // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
}
-
- dispatchTransaction = (tx: Transaction) => {
- if (this._editorView) {
- const state = this._editorView.state.apply(tx);
- this._editorView.updateState(state);
- const { doc, fieldKey } = this.props;
- doc.SetDataOnPrototype(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
- // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
- }
+ };
+
+ componentDidMount() {
+ let state: EditorState;
+ const config = {
+ schema,
+ inpRules, //these currently don't do anything, but could eventually be helpful
+ plugins: [
+ history(),
+ keymap({ "Mod-z": undo, "Mod-y": redo }),
+ keymap(baseKeymap),
+ this.tooltipMenuPlugin()
+ ]
+ };
+
+ let field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
+ if (field && field != FieldWaiting && field.Data) {
+ state = EditorState.fromJSON(config, JSON.parse(field.Data));
+ } else {
+ state = EditorState.create(config);
}
-
- componentDidMount() {
- let state: EditorState;
- const config = {
- schema,
- inpRules, //these currently don't do anything, but could eventually be helpful
- plugins: [
- history(),
- keymap({ "Mod-z": undo, "Mod-y": redo }),
- keymap(baseKeymap),
- this.tooltipMenuPlugin()
- ]
- };
-
- let field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
- if (field && field != FieldWaiting && field.Data) {
- state = EditorState.fromJSON(config, JSON.parse(field.Data));
- } else {
- state = EditorState.create(config);
- }
- if (this._ref.current) {
- this._editorView = new EditorView(this._ref.current, {
- state,
- dispatchTransaction: this.dispatchTransaction
- });
- }
-
- this._reactionDisposer = reaction(() => {
- const field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
- return field && field != FieldWaiting ? field.Data : undefined;
- }, (field) => {
- if (field && this._editorView) {
- this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)));
- }
- })
- if (this.props.selectOnLoad) {
- this.props.select();
- this._editorView!.focus();
- }
+ if (this._ref.current) {
+ this._editorView = new EditorView(this._ref.current, {
+ state,
+ dispatchTransaction: this.dispatchTransaction
+ });
}
- componentWillUnmount() {
- if (this._editorView) {
- this._editorView.destroy();
+ this._reactionDisposer = reaction(
+ () => {
+ const field = this.props.doc.GetT(this.props.fieldKey, RichTextField);
+ return field && field != FieldWaiting ? field.Data : undefined;
+ },
+ field => {
+ if (field && this._editorView) {
+ this._editorView.updateState(
+ EditorState.fromJSON(config, JSON.parse(field))
+ );
}
- if (this._reactionDisposer) {
- this._reactionDisposer();
- }
- }
-
- shouldComponentUpdate() {
- return false;
- }
-
- @action
- onChange(e: React.ChangeEvent<HTMLInputElement>) {
- const { fieldKey, doc } = this.props;
- doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value))
- // doc.SetData(fieldKey, e.target.value, RichTextField);
- }
- onPointerDown = (e: React.PointerEvent): void => {
- if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
- e.stopPropagation();
- }
- }
-
- //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
- textCapability = (e: React.MouseEvent): void => {
- }
-
- specificContextMenu = (e: React.MouseEvent): void => {
- ContextMenu.Instance.addItem({ description: "Text Capability", event: this.textCapability });
- // ContextMenu.Instance.addItem({
- // description: "Submenu",
- // items: [
- // {
- // description: "item 1", event:
- // },
- // {
- // description: "item 2", event:
- // }
- // ]
- // })
- // e.stopPropagation()
-
- }
-
- onPointerWheel = (e: React.WheelEvent): void => {
- e.stopPropagation();
+ }
+ );
+ if (this.props.selectOnLoad) {
+ this.props.select();
+ this._editorView!.focus();
}
+ }
- tooltipMenuPlugin() {
- return new Plugin({
- view(_editorView) {
- return new TooltipTextMenu(_editorView)
- }
- })
+ componentWillUnmount() {
+ if (this._editorView) {
+ this._editorView.destroy();
}
- onKeyPress(e: React.KeyboardEvent) {
- e.stopPropagation();
- // stop propagation doesn't seem to stop propagation of native keyboard events.
- // so we set a flag on the native event that marks that the event's been handled.
- // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
+ if (this._reactionDisposer) {
+ this._reactionDisposer();
}
- render() {
- return (<div className="formattedTextBox-cont"
- onKeyDown={this.onKeyPress}
- onKeyPress={this.onKeyPress}
- onPointerDown={this.onPointerDown}
- onContextMenu={this.specificContextMenu}
- // tfs: do we need this event handler
- onWheel={this.onPointerWheel}
- ref={this._ref} />)
+ }
+
+ shouldComponentUpdate() {
+ return false;
+ }
+
+ @action
+ onChange(e: React.ChangeEvent<HTMLInputElement>) {
+ const { fieldKey, doc } = this.props;
+ doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value));
+ // doc.SetData(fieldKey, e.target.value, RichTextField);
+ }
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
+ e.stopPropagation();
}
-} \ No newline at end of file
+ };
+
+ //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE
+ textCapability = (e: React.MouseEvent): void => {};
+
+ specificContextMenu = (e: React.MouseEvent): void => {
+ ContextMenu.Instance.addItem({
+ description: "Text Capability",
+ event: this.textCapability
+ });
+
+ // ContextMenu.Instance.addItem({
+ // description: "Submenu",
+ // items: [
+ // {
+ // description: "item 1", event:
+ // },
+ // {
+ // description: "item 2", event:
+ // }
+ // ]
+ // })
+ // e.stopPropagation()
+ };
+
+ onPointerWheel = (e: React.WheelEvent): void => {
+ e.stopPropagation();
+ };
+
+ tooltipMenuPlugin() {
+ return new Plugin({
+ view(_editorView) {
+ return new TooltipTextMenu(_editorView);
+ }
+ });
+ }
+ onKeyPress(e: React.KeyboardEvent) {
+ e.stopPropagation();
+ // stop propagation doesn't seem to stop propagation of native keyboard events.
+ // so we set a flag on the native event that marks that the event's been handled.
+ // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
+ }
+ render() {
+ return (
+ <div
+ className="formattedTextBox-cont"
+ onKeyDown={this.onKeyPress}
+ onKeyPress={this.onKeyPress}
+ onPointerDown={this.onPointerDown}
+ onContextMenu={this.specificContextMenu}
+ // tfs: do we need this event handler
+ onWheel={this.onPointerWheel}
+ ref={this._ref}
+ />
+ );
+ }
+}
diff --git a/src/client/views/nodes/Sticky.tsx b/src/client/views/nodes/Sticky.tsx
index d57dd5c0b..4a4d69e90 100644
--- a/src/client/views/nodes/Sticky.tsx
+++ b/src/client/views/nodes/Sticky.tsx
@@ -1,83 +1,83 @@
-import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
-import React = require("react")
-import { observer } from "mobx-react"
-import 'react-pdf/dist/Page/AnnotationLayer.css'
+import "react-image-lightbox/style.css"; // This only needs to be imported once in your app
+import React = require("react");
+import { observer } from "mobx-react";
+import "react-pdf/dist/Page/AnnotationLayer.css";
interface IProps {
- Height: number;
- Width: number;
- X: number;
- Y: number;
+ Height: number;
+ Width: number;
+ X: number;
+ Y: number;
}
/**
- * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file.
- * Improvements that could be made: maybe store line array and store that somewhere for future rerendering.
- *
- * Written By: Andrew Kim
+ * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file.
+ * Improvements that could be made: maybe store line array and store that somewhere for future rerendering.
+ *
+ * Written By: Andrew Kim
*/
@observer
export class Sticky extends React.Component<IProps> {
+ private initX: number = 0;
+ private initY: number = 0;
- private initX: number = 0;
- private initY: number = 0;
+ private _ref = React.createRef<HTMLCanvasElement>();
+ private ctx: any; //context that keeps track of sticky canvas
- private _ref = React.createRef<HTMLCanvasElement>();
- private ctx: any; //context that keeps track of sticky canvas
-
- /**
- * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas
- */
- drawDown = (e: React.PointerEvent) => {
- if (this._ref.current) {
- this.ctx = this._ref.current.getContext("2d");
- let mouse = e.nativeEvent;
- this.initX = mouse.offsetX;
- this.initY = mouse.offsetY;
- this.ctx.beginPath();
- this.ctx.lineTo(this.initX, this.initY);
- this.ctx.strokeStyle = "black";
- document.addEventListener("pointermove", this.drawMove);
- document.addEventListener("pointerup", this.drawUp);
- }
+ /**
+ * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas
+ */
+ drawDown = (e: React.PointerEvent) => {
+ if (this._ref.current) {
+ this.ctx = this._ref.current.getContext("2d");
+ let mouse = e.nativeEvent;
+ this.initX = mouse.offsetX;
+ this.initY = mouse.offsetY;
+ this.ctx.beginPath();
+ this.ctx.lineTo(this.initX, this.initY);
+ this.ctx.strokeStyle = "black";
+ document.addEventListener("pointermove", this.drawMove);
+ document.addEventListener("pointerup", this.drawUp);
}
+ };
- //when user drags
- drawMove = (e: PointerEvent): void => {
- //x and y mouse movement
- let x = this.initX += e.movementX,
- y = this.initY += e.movementY;
- //connects the point
- this.ctx.lineTo(x, y);
- this.ctx.stroke();
-
- }
+ //when user drags
+ drawMove = (e: PointerEvent): void => {
+ //x and y mouse movement
+ let x = (this.initX += e.movementX),
+ y = (this.initY += e.movementY);
+ //connects the point
+ this.ctx.lineTo(x, y);
+ this.ctx.stroke();
+ };
- /**
- * when user lifts the mouse, the drawing ends
- */
- drawUp = (e: PointerEvent) => {
- this.ctx.closePath();
- console.log(this.ctx);
- document.removeEventListener("pointermove", this.drawMove);
- }
+ /**
+ * when user lifts the mouse, the drawing ends
+ */
+ drawUp = (e: PointerEvent) => {
+ this.ctx.closePath();
+ console.log(this.ctx);
+ document.removeEventListener("pointermove", this.drawMove);
+ };
- render() {
- return (
- <div onPointerDown={this.drawDown}>
- <canvas ref={this._ref} height={this.props.Height} width={this.props.Width}
- style={{
- position: "absolute",
- top: "20px",
- left: "0px",
- zIndex: 1,
- background: "yellow",
- transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
- opacity: 0.4
- }}
- />
-
- </div>
- );
- }
-} \ No newline at end of file
+ render() {
+ return (
+ <div onPointerDown={this.drawDown}>
+ <canvas
+ ref={this._ref}
+ height={this.props.Height}
+ width={this.props.Width}
+ style={{
+ position: "absolute",
+ top: "20px",
+ left: "0px",
+ zIndex: 1,
+ background: "yellow",
+ transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
+ opacity: 0.4
+ }}
+ />
+ </div>
+ );
+ }
+}
diff --git a/src/fields/Document.ts b/src/fields/Document.ts
index 85ff6ddcb..8e71019a4 100644
--- a/src/fields/Document.ts
+++ b/src/fields/Document.ts
@@ -1,6 +1,6 @@
-import { Key } from "./Key"
+import { Key } from "./Key";
import { KeyStore } from "./KeyStore";
-import { Field, Cast, FieldWaiting, FieldValue, FieldId, Opt } from "./Field"
+import { Field, Cast, FieldWaiting, FieldValue, FieldId, Opt } from "./Field";
import { NumberField } from "./NumberField";
import { ObservableMap, computed, action, runInAction } from "mobx";
import { TextField } from "./TextField";
@@ -11,359 +11,413 @@ import { UndoManager } from "../client/util/UndoManager";
import { HtmlField } from "./HtmlField";
export class Document extends Field {
- //TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field
- public fields: ObservableMap<string, { key: Key, field: Field }> = new ObservableMap();
- public _proxies: ObservableMap<string, FieldId> = new ObservableMap();
-
- constructor(id?: string, save: boolean = true) {
- super(id)
-
- if (save) {
- Server.UpdateField(this)
- }
- }
-
- UpdateFromServer(data: [string, string][]) {
- for (const key in data) {
- const element = data[key];
- this._proxies.set(element[0], element[1]);
- }
- }
-
- public Width = () => { return this.GetNumber(KeyStore.Width, 0) }
- public Height = () => { return this.GetNumber(KeyStore.Height, this.GetNumber(KeyStore.NativeWidth, 0) ? this.GetNumber(KeyStore.NativeHeight, 0) / this.GetNumber(KeyStore.NativeWidth, 0) * this.GetNumber(KeyStore.Width, 0) : 0) }
- public Scale = () => { return this.GetNumber(KeyStore.Scale, 1) }
-
- @computed
- public get Title(): string {
- let title = this.Get(KeyStore.Title, true);
- if (title)
- if (title != FieldWaiting && title instanceof TextField)
- return title.Data;
- else return "-waiting-";
- let parTitle = this.GetT(KeyStore.Title, TextField);
- if (parTitle)
- if (parTitle != FieldWaiting)
- return parTitle.Data + ".alias";
- else return "-waiting-.alias";
- return "-untitled-";
+ //TODO tfs: We should probably store FieldWaiting in fields when we request it from the server so that we don't set up multiple server gets for the same document and field
+ public fields: ObservableMap<
+ string,
+ { key: Key; field: Field }
+ > = new ObservableMap();
+ public _proxies: ObservableMap<string, FieldId> = new ObservableMap();
+
+ constructor(id?: string, save: boolean = true) {
+ super(id);
+
+ if (save) {
+ Server.UpdateField(this);
}
+ }
- @computed
- public get Fields() {
- return this.fields;
+ UpdateFromServer(data: [string, string][]) {
+ for (const key in data) {
+ const element = data[key];
+ this._proxies.set(element[0], element[1]);
}
-
- /**
- * Get the field in the document associated with the given key. If the
- * associated field has not yet been filled in from the server, a request
- * to the server will automatically be sent, the value will be filled in
- * when the request is completed, and {@link Field.ts#FieldWaiting} will be returned.
- * @param key - The key of the value to get
- * @param ignoreProto - If true, ignore any prototype this document
- * might have and only search for the value on this immediate document.
- * If false (default), search up the prototype chain, starting at this document,
- * for a document that has a field associated with the given key, and return the first
- * one found.
- *
- * @returns If the document does not have a field associated with the given key, returns `undefined`.
- * If the document does have an associated field, but the field has not been fetched from the server, returns {@link Field.ts#FieldWaiting}.
- * If the document does have an associated field, and the field has not been fetched from the server, returns the associated field.
- */
- Get(key: Key, ignoreProto: boolean = false): FieldValue<Field> {
- let field: FieldValue<Field>;
- if (ignoreProto) {
- if (this.fields.has(key.Id)) {
- field = this.fields.get(key.Id)!.field;
- } else if (this._proxies.has(key.Id)) {
- Server.GetDocumentField(this, key);
- /*
+ }
+
+ public Width = () => {
+ return this.GetNumber(KeyStore.Width, 0);
+ };
+ public Height = () => {
+ return this.GetNumber(
+ KeyStore.Height,
+ this.GetNumber(KeyStore.NativeWidth, 0)
+ ? (this.GetNumber(KeyStore.NativeHeight, 0) /
+ this.GetNumber(KeyStore.NativeWidth, 0)) *
+ this.GetNumber(KeyStore.Width, 0)
+ : 0
+ );
+ };
+ public Scale = () => {
+ return this.GetNumber(KeyStore.Scale, 1);
+ };
+
+ @computed
+ public get Title(): string {
+ let title = this.Get(KeyStore.Title, true);
+ if (title)
+ if (title != FieldWaiting && title instanceof TextField)
+ return title.Data;
+ else return "-waiting-";
+ let parTitle = this.GetT(KeyStore.Title, TextField);
+ if (parTitle)
+ if (parTitle != FieldWaiting) return parTitle.Data + ".alias";
+ else return "-waiting-.alias";
+ return "-untitled-";
+ }
+
+ @computed
+ public get Fields() {
+ return this.fields;
+ }
+
+ /**
+ * Get the field in the document associated with the given key. If the
+ * associated field has not yet been filled in from the server, a request
+ * to the server will automatically be sent, the value will be filled in
+ * when the request is completed, and {@link Field.ts#FieldWaiting} will be returned.
+ * @param key - The key of the value to get
+ * @param ignoreProto - If true, ignore any prototype this document
+ * might have and only search for the value on this immediate document.
+ * If false (default), search up the prototype chain, starting at this document,
+ * for a document that has a field associated with the given key, and return the first
+ * one found.
+ *
+ * @returns If the document does not have a field associated with the given key, returns `undefined`.
+ * If the document does have an associated field, but the field has not been fetched from the server, returns {@link Field.ts#FieldWaiting}.
+ * If the document does have an associated field, and the field has not been fetched from the server, returns the associated field.
+ */
+ Get(key: Key, ignoreProto: boolean = false): FieldValue<Field> {
+ let field: FieldValue<Field>;
+ if (ignoreProto) {
+ if (this.fields.has(key.Id)) {
+ field = this.fields.get(key.Id)!.field;
+ } else if (this._proxies.has(key.Id)) {
+ Server.GetDocumentField(this, key);
+ /*
The field might have been instantly filled from the cache
Maybe we want to just switch back to returning the value
from Server.GetDocumentField if it's in the cache
*/
- if (this.fields.has(key.Id)) {
- field = this.fields.get(key.Id)!.field;
- } else {
- field = FieldWaiting;
- }
- }
+ if (this.fields.has(key.Id)) {
+ field = this.fields.get(key.Id)!.field;
} else {
- let doc: FieldValue<Document> = this;
- while (doc && doc != FieldWaiting && field != FieldWaiting) {
- let curField = doc.fields.get(key.Id);
- let curProxy = doc._proxies.get(key.Id);
- if (!curField || (curProxy && curField.field.Id !== curProxy)) {
- if (curProxy) {
- Server.GetDocumentField(doc, key);
- /*
+ field = FieldWaiting;
+ }
+ }
+ } else {
+ let doc: FieldValue<Document> = this;
+ while (doc && doc != FieldWaiting && field != FieldWaiting) {
+ let curField = doc.fields.get(key.Id);
+ let curProxy = doc._proxies.get(key.Id);
+ if (!curField || (curProxy && curField.field.Id !== curProxy)) {
+ if (curProxy) {
+ Server.GetDocumentField(doc, key);
+ /*
The field might have been instantly filled from the cache
Maybe we want to just switch back to returning the value
from Server.GetDocumentField if it's in the cache
*/
- if (this.fields.has(key.Id)) {
- field = this.fields.get(key.Id)!.field;
- } else {
- field = FieldWaiting;
- }
- break;
- }
- if ((doc.fields.has(KeyStore.Prototype.Id) || doc._proxies.has(KeyStore.Prototype.Id))) {
- doc = doc.GetPrototype();
- } else {
- break;
- }
- } else {
- field = curField.field;
- break;
- }
+ if (this.fields.has(key.Id)) {
+ field = this.fields.get(key.Id)!.field;
+ } else {
+ field = FieldWaiting;
}
- if (doc == FieldWaiting)
- field = FieldWaiting;
- }
-
- return field;
- }
-
- /**
- * Tries to get the field associated with the given key, and if there is an
- * associated field, calls the given callback with that field.
- * @param key - The key of the value to get
- * @param callback - A function that will be called with the associated field, if it exists,
- * once it is fetched from the server (this may be immediately if the field has already been fetched).
- * Note: The callback will not be called if there is no associated field.
- * @returns `true` if the field exists on the document and `callback` will be called, and `false` otherwise
- */
- GetAsync(key: Key, callback: (field: Opt<Field>) => void): void {
- //TODO: This currently doesn't deal with prototypes
- let field = this.fields.get(key.Id);
- if (field && field.field) {
- callback(field.field);
- } else if (this._proxies.has(key.Id)) {
- Server.GetDocumentField(this, key, callback);
- } else if (this._proxies.has(KeyStore.Prototype.Id)) {
- this.GetTAsync(KeyStore.Prototype, Document, proto => {
- if (proto) {
- proto.GetAsync(key, callback);
- } else {
- callback(undefined);
- }
- })
- } else {
- callback(undefined);
- }
- }
-
- GetTAsync<T extends Field>(key: Key, ctor: { new(): T }): Promise<Opt<T>>;
- GetTAsync<T extends Field>(key: Key, ctor: { new(): T }, callback: (field: Opt<T>) => void): void;
- GetTAsync<T extends Field>(key: Key, ctor: { new(): T }, callback?: (field: Opt<T>) => void): Promise<Opt<T>> | void {
- let fn = (cb: (field: Opt<T>) => void) => {
- return this.GetAsync(key, (field) => {
- cb(Cast(field, ctor));
- });
- }
- if (callback) {
- fn(callback);
+ break;
+ }
+ if (
+ doc.fields.has(KeyStore.Prototype.Id) ||
+ doc._proxies.has(KeyStore.Prototype.Id)
+ ) {
+ doc = doc.GetPrototype();
+ } else {
+ break;
+ }
} else {
- return new Promise(res => fn(res));
+ field = curField.field;
+ break;
}
+ }
+ if (doc == FieldWaiting) field = FieldWaiting;
}
- /**
- * Same as {@link Document#GetAsync}, except a field of the given type
- * will be created if there is no field associated with the given key,
- * or the field associated with the given key is not of the given type.
- * @param ctor - Constructor of the field type to get. E.g., TextField, ImageField, etc.
- */
- GetOrCreateAsync<T extends Field>(key: Key, ctor: { new(): T }, callback: (field: T) => void): void {
- //This currently doesn't deal with prototypes
- if (this._proxies.has(key.Id)) {
- Server.GetDocumentField(this, key, (field) => {
- if (field && field instanceof ctor) {
- callback(field);
- } else {
- let newField = new ctor();
- this.Set(key, newField);
- callback(newField);
- }
- });
+ return field;
+ }
+
+ /**
+ * Tries to get the field associated with the given key, and if there is an
+ * associated field, calls the given callback with that field.
+ * @param key - The key of the value to get
+ * @param callback - A function that will be called with the associated field, if it exists,
+ * once it is fetched from the server (this may be immediately if the field has already been fetched).
+ * Note: The callback will not be called if there is no associated field.
+ * @returns `true` if the field exists on the document and `callback` will be called, and `false` otherwise
+ */
+ GetAsync(key: Key, callback: (field: Opt<Field>) => void): void {
+ //TODO: This currently doesn't deal with prototypes
+ let field = this.fields.get(key.Id);
+ if (field && field.field) {
+ callback(field.field);
+ } else if (this._proxies.has(key.Id)) {
+ Server.GetDocumentField(this, key, callback);
+ } else if (this._proxies.has(KeyStore.Prototype.Id)) {
+ this.GetTAsync(KeyStore.Prototype, Document, proto => {
+ if (proto) {
+ proto.GetAsync(key, callback);
} else {
- let newField = new ctor();
- this.Set(key, newField);
- callback(newField);
- }
- }
-
- /**
- * Same as {@link Document#Get}, except that it will additionally
- * check if the field is of the given type.
- * @param ctor - Constructor of the field type to get. E.g., `TextField`, `ImageField`, etc.
- * @returns Same as {@link Document#Get}, except will return `undefined`
- * if there is an associated field but it is of the wrong type.
- */
- GetT<T extends Field = Field>(key: Key, ctor: { new(...args: any[]): T }, ignoreProto: boolean = false): FieldValue<T> {
- var getfield = this.Get(key, ignoreProto);
- if (getfield != FieldWaiting) {
- return Cast(getfield, ctor);
- }
- return FieldWaiting;
- }
-
- GetOrCreate<T extends Field>(key: Key, ctor: { new(): T }, ignoreProto: boolean = false): T {
- const field = this.GetT(key, ctor, ignoreProto);
- if (field && field != FieldWaiting) {
- return field;
- }
- const newField = new ctor();
- this.Set(key, newField);
- return newField;
- }
-
- GetData<T, U extends Field & { Data: T }>(key: Key, ctor: { new(): U }, defaultVal: T): T {
- let val = this.Get(key);
- let vval = (val && val instanceof ctor) ? val.Data : defaultVal;
- return vval;
- }
-
- GetHtml(key: Key, defaultVal: string): string {
- return this.GetData(key, HtmlField, defaultVal);
- }
-
- GetNumber(key: Key, defaultVal: number): number {
- return this.GetData(key, NumberField, defaultVal);
- }
-
- GetText(key: Key, defaultVal: string): string {
- return this.GetData(key, TextField, defaultVal);
- }
-
- GetList<T extends Field>(key: Key, defaultVal: T[]): T[] {
- return this.GetData<T[], ListField<T>>(key, ListField, defaultVal)
- }
-
- @action
- Set(key: Key, field: Field | undefined, setOnPrototype = false): void {
- let old = this.fields.get(key.Id);
- let oldField = old ? old.field : undefined;
- if (setOnPrototype) {
- this.SetOnPrototype(key, field)
- }
- else {
- if (field) {
- this.fields.set(key.Id, { key, field });
- this._proxies.set(key.Id, field.Id)
- // Server.AddDocumentField(this, key, field);
- } else {
- this.fields.delete(key.Id);
- this._proxies.delete(key.Id)
- // Server.DeleteDocumentField(this, key);
- }
- Server.UpdateField(this);
- }
- if (oldField || field) {
- UndoManager.AddEvent({
- undo: () => this.Set(key, oldField, setOnPrototype),
- redo: () => this.Set(key, field, setOnPrototype)
- })
+ callback(undefined);
}
+ });
+ } else {
+ callback(undefined);
}
-
- @action
- SetOnPrototype(key: Key, field: Field | undefined): void {
- this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
- f && f.Set(key, field)
- })
- }
-
- @action
- SetDataOnPrototype<T, U extends Field & { Data: T }>(key: Key, value: T, ctor: { new(): U }, replaceWrongType = true) {
- this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
- f && f.SetData(key, value, ctor)
- })
+ }
+
+ GetTAsync<T extends Field>(key: Key, ctor: { new (): T }): Promise<Opt<T>>;
+ GetTAsync<T extends Field>(
+ key: Key,
+ ctor: { new (): T },
+ callback: (field: Opt<T>) => void
+ ): void;
+ GetTAsync<T extends Field>(
+ key: Key,
+ ctor: { new (): T },
+ callback?: (field: Opt<T>) => void
+ ): Promise<Opt<T>> | void {
+ let fn = (cb: (field: Opt<T>) => void) => {
+ return this.GetAsync(key, field => {
+ cb(Cast(field, ctor));
+ });
+ };
+ if (callback) {
+ fn(callback);
+ } else {
+ return new Promise(res => fn(res));
}
-
- @action
- SetData<T, U extends Field & { Data: T }>(key: Key, value: T, ctor: { new(data: T): U }, replaceWrongType = true) {
- let field = this.Get(key, true);
- if (field instanceof ctor) {
- field.Data = value;
- } else if (!field || replaceWrongType) {
- let newField = new ctor(value);
- // newField.Data = value;
- this.Set(key, newField);
- }
- }
-
- @action
- SetText(key: Key, value: string, replaceWrongType = true) {
- this.SetData(key, value, TextField, replaceWrongType);
- }
-
- @action
- SetNumber(key: Key, value: number, replaceWrongType = true) {
- this.SetData(key, value, NumberField, replaceWrongType);
- }
-
- GetPrototype(): FieldValue<Document> {
- return this.GetT(KeyStore.Prototype, Document, true);
- }
-
- GetAllPrototypes(): Document[] {
- let protos: Document[] = [];
- let doc: FieldValue<Document> = this;
- while (doc && doc != FieldWaiting) {
- protos.push(doc);
- doc = doc.GetPrototype();
+ }
+
+ /**
+ * Same as {@link Document#GetAsync}, except a field of the given type
+ * will be created if there is no field associated with the given key,
+ * or the field associated with the given key is not of the given type.
+ * @param ctor - Constructor of the field type to get. E.g., TextField, ImageField, etc.
+ */
+ GetOrCreateAsync<T extends Field>(
+ key: Key,
+ ctor: { new (): T },
+ callback: (field: T) => void
+ ): void {
+ //This currently doesn't deal with prototypes
+ if (this._proxies.has(key.Id)) {
+ Server.GetDocumentField(this, key, field => {
+ if (field && field instanceof ctor) {
+ callback(field);
+ } else {
+ let newField = new ctor();
+ this.Set(key, newField);
+ callback(newField);
}
- return protos;
- }
-
- CreateAlias(id?: string): Document {
- let alias = new Document(id)
- this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
- f && alias.Set(KeyStore.Prototype, f)
- })
-
- return alias
+ });
+ } else {
+ let newField = new ctor();
+ this.Set(key, newField);
+ callback(newField);
}
-
- MakeDelegate(id?: string): Document {
- let delegate = new Document(id);
-
- delegate.Set(KeyStore.Prototype, this);
-
- return delegate;
+ }
+
+ /**
+ * Same as {@link Document#Get}, except that it will additionally
+ * check if the field is of the given type.
+ * @param ctor - Constructor of the field type to get. E.g., `TextField`, `ImageField`, etc.
+ * @returns Same as {@link Document#Get}, except will return `undefined`
+ * if there is an associated field but it is of the wrong type.
+ */
+ GetT<T extends Field = Field>(
+ key: Key,
+ ctor: { new (...args: any[]): T },
+ ignoreProto: boolean = false
+ ): FieldValue<T> {
+ var getfield = this.Get(key, ignoreProto);
+ if (getfield != FieldWaiting) {
+ return Cast(getfield, ctor);
}
-
- ToScriptString(): string {
- return "";
+ return FieldWaiting;
+ }
+
+ GetOrCreate<T extends Field>(
+ key: Key,
+ ctor: { new (): T },
+ ignoreProto: boolean = false
+ ): T {
+ const field = this.GetT(key, ctor, ignoreProto);
+ if (field && field != FieldWaiting) {
+ return field;
}
-
- TrySetValue(value: any): boolean {
- throw new Error("Method not implemented.");
+ const newField = new ctor();
+ this.Set(key, newField);
+ return newField;
+ }
+
+ GetData<T, U extends Field & { Data: T }>(
+ key: Key,
+ ctor: { new (): U },
+ defaultVal: T
+ ): T {
+ let val = this.Get(key);
+ let vval = val && val instanceof ctor ? val.Data : defaultVal;
+ return vval;
+ }
+
+ GetHtml(key: Key, defaultVal: string): string {
+ return this.GetData(key, HtmlField, defaultVal);
+ }
+
+ GetNumber(key: Key, defaultVal: number): number {
+ return this.GetData(key, NumberField, defaultVal);
+ }
+
+ GetText(key: Key, defaultVal: string): string {
+ return this.GetData(key, TextField, defaultVal);
+ }
+
+ GetList<T extends Field>(key: Key, defaultVal: T[]): T[] {
+ return this.GetData<T[], ListField<T>>(key, ListField, defaultVal);
+ }
+
+ @action
+ Set(key: Key, field: Field | undefined, setOnPrototype = false): void {
+ let old = this.fields.get(key.Id);
+ let oldField = old ? old.field : undefined;
+ if (setOnPrototype) {
+ this.SetOnPrototype(key, field);
+ } else {
+ if (field) {
+ this.fields.set(key.Id, { key, field });
+ this._proxies.set(key.Id, field.Id);
+ // Server.AddDocumentField(this, key, field);
+ } else {
+ this.fields.delete(key.Id);
+ this._proxies.delete(key.Id);
+ // Server.DeleteDocumentField(this, key);
+ }
+ Server.UpdateField(this);
}
- GetValue() {
- return this.Title;
- var title = (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) + "(" + this.Id + ")";
- return title;
- //throw new Error("Method not implemented.");
+ if (oldField || field) {
+ UndoManager.AddEvent({
+ undo: () => this.Set(key, oldField, setOnPrototype),
+ redo: () => this.Set(key, field, setOnPrototype)
+ });
}
- Copy(): Field {
- throw new Error("Method not implemented.");
+ }
+
+ @action
+ SetOnPrototype(key: Key, field: Field | undefined): void {
+ this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+ f && f.Set(key, field);
+ });
+ }
+
+ @action
+ SetDataOnPrototype<T, U extends Field & { Data: T }>(
+ key: Key,
+ value: T,
+ ctor: { new (): U },
+ replaceWrongType = true
+ ) {
+ this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+ f && f.SetData(key, value, ctor);
+ });
+ }
+
+ @action
+ SetData<T, U extends Field & { Data: T }>(
+ key: Key,
+ value: T,
+ ctor: { new (data: T): U },
+ replaceWrongType = true
+ ) {
+ let field = this.Get(key, true);
+ if (field instanceof ctor) {
+ field.Data = value;
+ } else if (!field || replaceWrongType) {
+ let newField = new ctor(value);
+ // newField.Data = value;
+ this.Set(key, newField);
}
-
- ToJson(): { type: Types, data: [string, string][], _id: string } {
- let fields: [string, string][] = []
- this._proxies.forEach((field, key) => {
- if (field) {
- fields.push([key, field as string])
- }
- });
-
- return {
- type: Types.Document,
- data: fields,
- _id: this.Id
- }
+ }
+
+ @action
+ SetText(key: Key, value: string, replaceWrongType = true) {
+ this.SetData(key, value, TextField, replaceWrongType);
+ }
+
+ @action
+ SetNumber(key: Key, value: number, replaceWrongType = true) {
+ this.SetData(key, value, NumberField, replaceWrongType);
+ }
+
+ GetPrototype(): FieldValue<Document> {
+ return this.GetT(KeyStore.Prototype, Document, true);
+ }
+
+ GetAllPrototypes(): Document[] {
+ let protos: Document[] = [];
+ let doc: FieldValue<Document> = this;
+ while (doc && doc != FieldWaiting) {
+ protos.push(doc);
+ doc = doc.GetPrototype();
}
-} \ No newline at end of file
+ return protos;
+ }
+
+ CreateAlias(id?: string): Document {
+ let alias = new Document(id);
+ this.GetTAsync(KeyStore.Prototype, Document, (f: Opt<Document>) => {
+ f && alias.Set(KeyStore.Prototype, f);
+ });
+
+ return alias;
+ }
+
+ MakeDelegate(id?: string): Document {
+ let delegate = new Document(id);
+
+ delegate.Set(KeyStore.Prototype, this);
+
+ return delegate;
+ }
+
+ ToScriptString(): string {
+ return "";
+ }
+
+ TrySetValue(value: any): boolean {
+ throw new Error("Method not implemented.");
+ }
+ GetValue() {
+ return this.Title;
+ var title =
+ (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) +
+ "(" +
+ this.Id +
+ ")";
+ return title;
+ //throw new Error("Method not implemented.");
+ }
+ Copy(): Field {
+ throw new Error("Method not implemented.");
+ }
+
+ ToJson(): { type: Types; data: [string, string][]; _id: string } {
+ let fields: [string, string][] = [];
+ this._proxies.forEach((field, key) => {
+ if (field) {
+ fields.push([key, field as string]);
+ }
+ });
+
+ return {
+ type: Types.Document,
+ data: fields,
+ _id: this.Id
+ };
+ }
+}
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 1f039e592..6ed3b1604 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -1,48 +1,50 @@
import { Key } from "./Key";
+import { KeyTransfer } from "../server/Message";
export namespace KeyStore {
- export const Prototype = new Key("Prototype");
- export const X = new Key("X");
- export const Y = new Key("Y");
- export const Page = new Key("Page");
- export const Title = new Key("Title");
- export const Author = new Key("Author");
- export const PanX = new Key("PanX");
- export const PanY = new Key("PanY");
- export const Scale = new Key("Scale");
- export const NativeWidth = new Key("NativeWidth");
- export const NativeHeight = new Key("NativeHeight");
- export const Width = new Key("Width");
- export const Height = new Key("Height");
- export const ZIndex = new Key("ZIndex");
- export const Data = new Key("Data");
- export const Annotations = new Key("Annotations");
- export const ViewType = new Key("ViewType");
- export const Layout = new Key("Layout");
- export const BackgroundColor = new Key("BackgroundColor");
- export const BackgroundLayout = new Key("BackgroundLayout");
- export const OverlayLayout = new Key("OverlayLayout");
- export const LayoutKeys = new Key("LayoutKeys");
- export const LayoutFields = new Key("LayoutFields");
- export const ColumnsKey = new Key("SchemaColumns");
- export const SchemaSplitPercentage = new Key("SchemaSplitPercentage");
- export const Caption = new Key("Caption");
- export const ActiveWorkspace = new Key("ActiveWorkspace");
- export const DocumentText = new Key("DocumentText");
- export const BrushingDocs = new Key("BrushingDocs");
- export const LinkedToDocs = new Key("LinkedToDocs");
- export const LinkedFromDocs = new Key("LinkedFromDocs");
- export const LinkDescription = new Key("LinkDescription");
- export const LinkTags = new Key("LinkTag");
- export const Thumbnail = new Key("Thumbnail");
- export const ThumbnailPage = new Key("ThumbnailPage");
- export const CurPage = new Key("CurPage");
- export const AnnotationOn = new Key("AnnotationOn");
- export const NumPages = new Key("NumPages");
- export const Ink = new Key("Ink");
- export const Cursors = new Key("Cursors");
- export const OptionalRightCollection = new Key("OptionalRightCollection");
- export const Archives = new Key("Archives");
- export const Updated = new Key("Updated");
- export const Workspaces = new Key("Workspaces");
+ export const Prototype = new Key("Prototype");
+ export const X = new Key("X");
+ export const Y = new Key("Y");
+ export const Page = new Key("Page");
+ export const Title = new Key("Title");
+ export const Author = new Key("Author");
+ export const PanX = new Key("PanX");
+ export const PanY = new Key("PanY");
+ export const Scale = new Key("Scale");
+ export const NativeWidth = new Key("NativeWidth");
+ export const NativeHeight = new Key("NativeHeight");
+ export const Width = new Key("Width");
+ export const Height = new Key("Height");
+ export const ZIndex = new Key("ZIndex");
+ export const Data = new Key("Data");
+ export const Annotations = new Key("Annotations");
+ export const ViewType = new Key("ViewType");
+ export const Layout = new Key("Layout");
+ export const BackgroundColor = new Key("BackgroundColor");
+ export const BackgroundLayout = new Key("BackgroundLayout");
+ export const OverlayLayout = new Key("OverlayLayout");
+ export const LayoutKeys = new Key("LayoutKeys");
+ export const LayoutFields = new Key("LayoutFields");
+ export const ColumnsKey = new Key("SchemaColumns");
+ export const SchemaSplitPercentage = new Key("SchemaSplitPercentage");
+ export const Caption = new Key("Caption");
+ export const ActiveWorkspace = new Key("ActiveWorkspace");
+ export const DocumentText = new Key("DocumentText");
+ export const BrushingDocs = new Key("BrushingDocs");
+ export const LinkedToDocs = new Key("LinkedToDocs");
+ export const LinkedFromDocs = new Key("LinkedFromDocs");
+ export const LinkDescription = new Key("LinkDescription");
+ export const LinkTags = new Key("LinkTag");
+ export const Thumbnail = new Key("Thumbnail");
+ export const ThumbnailPage = new Key("ThumbnailPage");
+ export const CurPage = new Key("CurPage");
+ export const AnnotationOn = new Key("AnnotationOn");
+ export const NumPages = new Key("NumPages");
+ export const Ink = new Key("Ink");
+ export const Cursors = new Key("Cursors");
+ export const OptionalRightCollection = new Key("OptionalRightCollection");
+ export const Archives = new Key("Archives");
+ export const Updated = new Key("Updated");
+ export const Workspaces = new Key("Workspaces");
+ export const Minimized = new Key("Minimized");
}
diff --git a/src/fields/MinimizedField.tsx b/src/fields/MinimizedField.tsx
new file mode 100644
index 000000000..5dfb22e6e
--- /dev/null
+++ b/src/fields/MinimizedField.tsx
@@ -0,0 +1,29 @@
+import { BasicField } from "./BasicField";
+import { FieldId } from "./Field";
+import { Types } from "../server/Message";
+
+export class MinimizedField extends BasicField<boolean> {
+ constructor(
+ data: boolean = false as boolean,
+ id?: FieldId,
+ save: boolean = true as boolean
+ ) {
+ super(data, save, id);
+ }
+
+ ToScriptString(): string {
+ return `new MinimizedField("${this.Data}")`;
+ }
+
+ Copy() {
+ return new MinimizedField(this.Data);
+ }
+
+ ToJson(): { type: Types; data: boolean; _id: string } {
+ return {
+ type: Types.Minimized,
+ data: this.Data,
+ _id: this.Id
+ };
+ }
+}
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 05ae0f19a..29df57419 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -1,125 +1,145 @@
import { Utils } from "../Utils";
export class Message<T> {
- private name: string;
- private guid: string;
+ private name: string;
+ private guid: string;
- get Name(): string {
- return this.name;
- }
+ get Name(): string {
+ return this.name;
+ }
- get Message(): string {
- return this.guid
- }
+ get Message(): string {
+ return this.guid;
+ }
- constructor(name: string) {
- this.name = name;
- this.guid = Utils.GenerateDeterministicGuid(name)
- }
+ constructor(name: string) {
+ this.name = name;
+ this.guid = Utils.GenerateDeterministicGuid(name);
+ }
- GetValue() {
- return this.Name;
- }
+ GetValue() {
+ return this.Name;
+ }
}
class TestMessageArgs {
- hello: string = "";
+ hello: string = "";
}
export class SetFieldArgs {
- field: string;
- value: any;
+ field: string;
+ value: any;
- constructor(f: string, v: any) {
- this.field = f
- this.value = v
- }
+ constructor(f: string, v: any) {
+ this.field = f;
+ this.value = v;
+ }
}
export class GetFieldArgs {
- field: string;
+ field: string;
- constructor(f: string) {
- this.field = f
- }
+ constructor(f: string) {
+ this.field = f;
+ }
}
export enum Types {
- Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference, Html, Video, Audio, Ink, PDF, Tuple, HistogramOp
+ Number,
+ List,
+ Key,
+ Image,
+ Web,
+ Document,
+ Text,
+ RichText,
+ DocumentReference,
+ Html,
+ Video,
+ Audio,
+ Ink,
+ PDF,
+ Tuple,
+ HistogramOp,
+ Minimized
}
export class DocumentTransfer implements Transferable {
- readonly type = Types.Document
- _id: string
-
- constructor(readonly obj: { type: Types, data: [string, string][], _id: string }) {
- this._id = obj._id
- }
+ readonly type = Types.Document;
+ _id: string;
+
+ constructor(
+ readonly obj: { type: Types; data: [string, string][]; _id: string }
+ ) {
+ this._id = obj._id;
+ }
}
export class ImageTransfer implements Transferable {
- readonly type = Types.Image
+ readonly type = Types.Image;
- constructor(readonly _id: string) { }
+ constructor(readonly _id: string) {}
}
export class KeyTransfer implements Transferable {
- name: string
- readonly _id: string
- readonly type = Types.Key
-
- constructor(i: string, n: string) {
- this.name = n
- this._id = i
- }
+ name: string;
+ readonly _id: string;
+ readonly type = Types.Key;
+
+ constructor(i: string, n: string) {
+ this.name = n;
+ this._id = i;
+ }
}
export class ListTransfer implements Transferable {
- type = Types.List;
+ type = Types.List;
- constructor(readonly _id: string) { }
+ constructor(readonly _id: string) {}
}
export class NumberTransfer implements Transferable {
- readonly type = Types.Number
+ readonly type = Types.Number;
- constructor(readonly value: number, readonly _id: string) { }
+ constructor(readonly value: number, readonly _id: string) {}
}
export class TextTransfer implements Transferable {
- value: string
- readonly _id: string
- readonly type = Types.Text
-
- constructor(t: string, i: string) {
- this.value = t
- this._id = i
- }
+ value: string;
+ readonly _id: string;
+ readonly type = Types.Text;
+
+ constructor(t: string, i: string) {
+ this.value = t;
+ this._id = i;
+ }
}
export class RichTextTransfer implements Transferable {
- value: string
- readonly _id: string
- readonly type = Types.Text
-
- constructor(t: string, i: string) {
- this.value = t
- this._id = i
- }
+ value: string;
+ readonly _id: string;
+ readonly type = Types.Text;
+
+ constructor(t: string, i: string) {
+ this.value = t;
+ this._id = i;
+ }
}
export interface Transferable {
- readonly _id: string
- readonly type: Types
+ readonly _id: string;
+ readonly type: Types;
}
export namespace MessageStore {
- export const Foo = new Message<string>("Foo");
- export const Bar = new Message<string>("Bar");
- export const AddDocument = new Message<DocumentTransfer>("Add Document");
- export const SetField = new Message<{ _id: string, data: any, type: Types }>("Set Field")
- export const GetField = new Message<string>("Get Field")
- export const GetFields = new Message<string[]>("Get Fields")
- export const GetDocument = new Message<string>("Get Document");
- export const DeleteAll = new Message<any>("Delete All");
-} \ No newline at end of file
+ export const Foo = new Message<string>("Foo");
+ export const Bar = new Message<string>("Bar");
+ export const AddDocument = new Message<DocumentTransfer>("Add Document");
+ export const SetField = new Message<{ _id: string; data: any; type: Types }>(
+ "Set Field"
+ );
+ export const GetField = new Message<string>("Get Field");
+ export const GetFields = new Message<string[]>("Get Fields");
+ export const GetDocument = new Message<string>("Get Document");
+ export const DeleteAll = new Message<any>("Delete All");
+}
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 98a7a1451..f3c5e1d1f 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -1,78 +1,84 @@
-
-import { Field } from './../fields/Field';
-import { TextField } from './../fields/TextField';
-import { NumberField } from './../fields/NumberField';
-import { RichTextField } from './../fields/RichTextField';
-import { Key } from './../fields/Key';
-import { ImageField } from './../fields/ImageField';
-import { ListField } from './../fields/ListField';
-import { Document } from './../fields/Document';
-import { Server } from './../client/Server';
-import { Types } from './Message';
-import { Utils } from '../Utils';
-import { HtmlField } from '../fields/HtmlField';
-import { WebField } from '../fields/WebField';
-import { AudioField } from '../fields/AudioField';
-import { VideoField } from '../fields/VideoField';
-import { InkField } from '../fields/InkField';
-import { PDFField } from '../fields/PDFField';
-import { TupleField } from '../fields/TupleField';
-import { HistogramField } from '../client/northstar/dash-fields/HistogramField';
-
-
+import { Field } from "./../fields/Field";
+import { TextField } from "./../fields/TextField";
+import { NumberField } from "./../fields/NumberField";
+import { RichTextField } from "./../fields/RichTextField";
+import { Key } from "./../fields/Key";
+import { ImageField } from "./../fields/ImageField";
+import { ListField } from "./../fields/ListField";
+import { Document } from "./../fields/Document";
+import { Server } from "./../client/Server";
+import { Types } from "./Message";
+import { Utils } from "../Utils";
+import { HtmlField } from "../fields/HtmlField";
+import { WebField } from "../fields/WebField";
+import { AudioField } from "../fields/AudioField";
+import { VideoField } from "../fields/VideoField";
+import { InkField } from "../fields/InkField";
+import { PDFField } from "../fields/PDFField";
+import { TupleField } from "../fields/TupleField";
+import { MinimizedField } from "../fields/MinimizedField";
+import { HistogramField } from "../client/northstar/dash-fields/HistogramField";
export class ServerUtils {
- public static prepend(extension: string): string { return window.location.origin + extension; }
+ public static prepend(extension: string): string {
+ return window.location.origin + extension;
+ }
- public static FromJson(json: any): Field {
- let obj = json
- let data: any = obj.data
- let id: string = obj._id
- let type: Types = obj.type
+ public static FromJson(json: any): Field {
+ let obj = json;
+ let data: any = obj.data;
+ let id: string = obj._id;
+ let type: Types = obj.type;
- if (!(data !== undefined && id && type !== undefined)) {
- console.log("how did you manage to get an object that doesn't have a data or an id?")
- return new TextField("Something to fill the space", Utils.GenerateGuid());
- }
+ if (!(data !== undefined && id && type !== undefined)) {
+ console.log(
+ "how did you manage to get an object that doesn't have a data or an id?"
+ );
+ return new TextField("Something to fill the space", Utils.GenerateGuid());
+ }
- switch (type) {
- case Types.Number:
- return new NumberField(data, id, false)
- case Types.Text:
- return new TextField(data, id, false)
- case Types.Html:
- return new HtmlField(data, id, false)
- case Types.Web:
- return new WebField(new URL(data), id, false)
- case Types.RichText:
- return new RichTextField(data, id, false)
- case Types.Key:
- return new Key(data, id, false)
- case Types.Image:
- return new ImageField(new URL(data), id, false)
- case Types.HistogramOp:
- return HistogramField.FromJson(id, data);
- case Types.PDF:
- return new PDFField(new URL(data), id, false)
- case Types.List:
- return ListField.FromJson(id, data)
- case Types.Audio:
- return new AudioField(new URL(data), id, false)
- case Types.Video:
- return new VideoField(new URL(data), id, false)
- case Types.Tuple:
- return new TupleField(data, id, false);
- case Types.Ink:
- return InkField.FromJson(id, data);
- case Types.Document:
- let doc: Document = new Document(id, false)
- let fields: [string, string][] = data as [string, string][]
- fields.forEach(element => {
- doc._proxies.set(element[0], element[1]);
- });
- return doc
- default:
- throw Error("Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here");
- }
+ switch (type) {
+ case Types.Minimized:
+ return new MinimizedField(data, id, false);
+ case Types.Number:
+ return new NumberField(data, id, false);
+ case Types.Text:
+ return new TextField(data, id, false);
+ case Types.Html:
+ return new HtmlField(data, id, false);
+ case Types.Web:
+ return new WebField(new URL(data), id, false);
+ case Types.RichText:
+ return new RichTextField(data, id, false);
+ case Types.Key:
+ return new Key(data, id, false);
+ case Types.Image:
+ return new ImageField(new URL(data), id, false);
+ case Types.HistogramOp:
+ return HistogramField.FromJson(id, data);
+ case Types.PDF:
+ return new PDFField(new URL(data), id, false);
+ case Types.List:
+ return ListField.FromJson(id, data);
+ case Types.Audio:
+ return new AudioField(new URL(data), id, false);
+ case Types.Video:
+ return new VideoField(new URL(data), id, false);
+ case Types.Tuple:
+ return new TupleField(data, id, false);
+ case Types.Ink:
+ return InkField.FromJson(id, data);
+ case Types.Document:
+ let doc: Document = new Document(id, false);
+ let fields: [string, string][] = data as [string, string][];
+ fields.forEach(element => {
+ doc._proxies.set(element[0], element[1]);
+ });
+ return doc;
+ default:
+ throw Error(
+ "Error, unrecognized field type received from server. If you just created a new field type, be sure to add it here"
+ );
}
-} \ No newline at end of file
+ }
+}