aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/DocumentManager.ts26
-rw-r--r--src/client/util/DragManager.ts110
-rw-r--r--src/client/util/ProsemirrorKeymap.ts100
-rw-r--r--src/client/util/RichTextSchema.tsx434
-rw-r--r--src/client/util/Scripting.ts39
-rw-r--r--src/client/util/ScrollBox.tsx4
-rw-r--r--src/client/util/SelectionManager.ts19
-rw-r--r--src/client/util/TooltipTextMenu.tsx40
-rw-r--r--src/client/util/Transform.ts42
-rw-r--r--src/client/util/TypedEvent.ts4
-rw-r--r--src/client/util/UndoManager.ts46
-rw-r--r--src/client/util/type_decls.d2
12 files changed, 469 insertions, 397 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 341959936..f38b8ca75 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,7 @@
-import React = require('react')
+import React = require('react');
import { observer } from 'mobx-react';
import { observable, action, computed } from 'mobx';
-import { Document } from "../../fields/Document"
+import { Document } from "../../fields/Document";
import { DocumentView } from '../views/nodes/DocumentView';
import { KeyStore } from '../../fields/KeyStore';
import { FieldWaiting } from '../../fields/Field';
@@ -29,7 +29,7 @@ export class DocumentManager {
public getAllDocumentViews(collection: Document) {
return this.DocumentViews.filter(dv =>
- dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document.Id === collection.Id);
+ dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document === collection);
}
public getDocumentView(toFind: Document): DocumentView | null {
@@ -42,15 +42,15 @@ export class DocumentManager {
let doc = view.props.Document;
// if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
- if (Object.is(doc, toFind)) {
+ if (doc === toFind) {
toReturn = view;
return;
}
let docSrc = doc.GetT(KeyStore.Prototype, Document);
- if (docSrc && docSrc != FieldWaiting && Object.is(docSrc, toFind)) {
+ if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) {
toReturn = view;
}
- })
+ });
return (toReturn);
}
@@ -63,15 +63,15 @@ export class DocumentManager {
let doc = view.props.Document;
// if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) {
- if (Object.is(doc, toFind)) {
+ if (doc === toFind) {
toReturn.push(view);
} else {
let docSrc = doc.GetT(KeyStore.Prototype, Document);
- if (docSrc && docSrc != FieldWaiting && Object.is(docSrc, toFind)) {
+ if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) {
toReturn.push(view);
}
}
- })
+ });
return (toReturn);
}
@@ -80,14 +80,14 @@ export class DocumentManager {
public get LinkedDocumentViews() {
return DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => {
let linksList = dv.props.Document.GetT(KeyStore.LinkedToDocs, ListField);
- if (linksList && linksList != FieldWaiting && linksList.Data.length) {
+ if (linksList && linksList !== FieldWaiting && linksList.Data.length) {
pairs.push(...linksList.Data.reduce((pairs, link) => {
if (link instanceof Document) {
let linkToDoc = link.GetT(KeyStore.LinkedToDocs, Document);
- if (linkToDoc && linkToDoc != FieldWaiting) {
+ if (linkToDoc && linkToDoc !== FieldWaiting) {
DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
- pairs.push({ a: dv, b: docView1, l: link })
- })
+ pairs.push({ a: dv, b: docView1, l: link });
+ });
}
}
return pairs;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0ee7ed2b3..4849ae9f7 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,36 +4,27 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
import { CollectionView } from "../views/collections/CollectionView";
import { DocumentDecorations } from "../views/DocumentDecorations";
import { DocumentView } from "../views/nodes/DocumentView";
+import { returnFalse } from "../../Utils";
-export function setupDrag(
- _reference: React.RefObject<HTMLDivElement>,
- docFunc: () => Document,
- removeFunc: (containingCollection: CollectionView) => void = () => { },
- copyOnDrop: boolean = false
-) {
- let onRowMove = action(
- (e: PointerEvent): void => {
- e.stopPropagation();
- e.preventDefault();
+export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) {
+ let onRowMove = action((e: PointerEvent): void => {
+ e.stopPropagation();
+ e.preventDefault();
- // TODO: bcz -- this needs to have a drag threshold so that it doesn't trigger when just selecting.
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener("pointerup", onRowUp);
- var dragData = new DragManager.DocumentDragData([docFunc()]);
- dragData.copyOnDrop = copyOnDrop;
- dragData.removeDocument = removeFunc;
- DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
- }
- );
- let onRowUp = action(
- (e: PointerEvent): void => {
- document.removeEventListener("pointermove", onRowMove);
- document.removeEventListener("pointerup", onRowUp);
- }
- );
+ document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener('pointerup', onRowUp);
+ var dragData = new DragManager.DocumentDragData([docFunc()]);
+ dragData.copyOnDrop = copyOnDrop;
+ dragData.moveDocument = moveFunc;
+ DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
+ });
+ 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) {
+ if (e.button === 0) {
e.stopPropagation();
if (e.shiftKey) {
CollectionDockingView.Instance.StartOtherDrag([docFunc()], e);
@@ -104,7 +95,7 @@ export namespace DragManager {
"Element is already droppable, can't make it droppable again"
);
}
- element.dataset["canDrop"] = "true";
+ element.dataset.canDrop = "true";
const handler = (e: Event) => {
const ce = e as CustomEvent<DropEvent>;
options.handlers.drop(e, ce.detail);
@@ -112,10 +103,11 @@ export namespace DragManager {
element.addEventListener("dashOnDrop", handler);
return () => {
element.removeEventListener("dashOnDrop", handler);
- delete element.dataset["canDrop"];
+ delete element.dataset.canDrop;
};
}
+ export type MoveFunction = (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean;
export class DocumentDragData {
constructor(dragDoc: Document[]) {
this.draggedDocuments = dragDoc;
@@ -127,24 +119,13 @@ export namespace DragManager {
yOffset?: number;
aliasOnDrop?: boolean;
copyOnDrop?: boolean;
- removeDocument?: (collectionDrop: CollectionView) => void;
+ moveDocument?: MoveFunction;
[id: string]: any;
}
- export function StartDocumentDrag(
- eles: HTMLElement[],
- dragData: DocumentDragData,
- downX: number,
- downY: number,
- options?: DragOptions
- ) {
+ export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
StartDrag(eles, dragData, downX, downY, options,
- (dropData: { [id: string]: any }) =>
- (dropData.droppedDocuments = dragData.aliasOnDrop
- ? dragData.draggedDocuments.map(d => d.CreateAlias())
- : dragData.copyOnDrop ? dragData.draggedDocuments.map(d => d.Copy(true) as Document) :
- dragData.draggedDocuments)
- );
+ (dropData: { [id: string]: any }) => (dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => d.CreateAlias()) : dragData.copyOnDrop ? dragData.draggedDocuments.map(d => d.Copy(true) as Document) : dragData.draggedDocuments));
}
export class LinkDragData {
@@ -155,21 +136,12 @@ export namespace DragManager {
linkSourceDocumentView: DocumentView;
[id: string]: any;
}
- export function StartLinkDrag(
- ele: HTMLElement,
- dragData: LinkDragData,
- downX: number, downY: number,
- options?: DragOptions
- ) {
+
+ export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, downX: number, downY: number, options?: DragOptions) {
StartDrag([ele], dragData, downX, downY, options);
}
- function StartDrag(
- eles: HTMLElement[],
- dragData: { [id: string]: any },
- downX: number, downY: number,
- options?: DragOptions,
- finishDrag?: (dropData: { [id: string]: any }) => void
- ) {
+
+ function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
if (!dragDiv) {
dragDiv = document.createElement("div");
dragDiv.className = "dragManager-dragDiv";
@@ -217,11 +189,11 @@ export namespace DragManager {
// 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]);
+ // 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])
// }
// }
@@ -244,8 +216,9 @@ export namespace DragManager {
const moveHandler = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- if (dragData instanceof DocumentDragData)
+ if (dragData instanceof DocumentDragData) {
dragData.aliasOnDrop = e.ctrlKey || e.altKey;
+ }
if (e.shiftKey) {
abortDrag();
CollectionDockingView.Instance.StartOtherDrag(docs, {
@@ -255,6 +228,7 @@ export namespace DragManager {
button: 0
});
}
+ //TODO: Why can't we use e.movementX and e.movementY?
let moveX = e.pageX - lastX;
let moveY = e.pageY - lastY;
lastX = e.pageX;
@@ -279,13 +253,7 @@ export namespace DragManager {
document.addEventListener("pointerup", upHandler);
}
- function FinishDrag(
- dragEles: HTMLElement[],
- e: PointerEvent,
- dragData: { [index: string]: any },
- options?: DragOptions,
- finishDrag?: (dragData: { [index: string]: any }) => void
- ) {
+ 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);
@@ -293,9 +261,9 @@ export namespace DragManager {
});
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);
+ let dragEle = r[0];
+ let parent = r[1];
+ if (parent && dragEle) parent.appendChild(dragEle);
});
if (target) {
if (finishDrag) finishDrag(dragData);
diff --git a/src/client/util/ProsemirrorKeymap.ts b/src/client/util/ProsemirrorKeymap.ts
new file mode 100644
index 000000000..7718b3c06
--- /dev/null
+++ b/src/client/util/ProsemirrorKeymap.ts
@@ -0,0 +1,100 @@
+import { Schema } from "prosemirror-model";
+import {
+ wrapIn, setBlockType, chainCommands, toggleMark, exitCode,
+ joinUp, joinDown, lift, selectParentNode
+} from "prosemirror-commands";
+import { wrapInList, splitListItem, liftListItem, sinkListItem } from "prosemirror-schema-list";
+import { undo, redo } from "prosemirror-history";
+import { undoInputRule } from "prosemirror-inputrules";
+import { Transaction, EditorState } from "prosemirror-state";
+
+const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
+
+export type KeyMap = { [key: string]: any };
+
+export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?: KeyMap): KeyMap {
+ let keys: { [key: string]: any } = {}, type;
+
+ function bind(key: string, cmd: any) {
+ if (mapKeys) {
+ let mapped = mapKeys[key];
+ if (mapped === false) return;
+ if (mapped) key = mapped;
+ }
+ keys[key] = cmd;
+ }
+
+ bind("Mod-z", undo);
+ bind("Shift-Mod-z", redo);
+ bind("Backspace", undoInputRule);
+
+ if (!mac) {
+ bind("Mod-y", redo);
+ }
+
+ bind("Alt-ArrowUp", joinUp);
+ bind("Alt-ArrowDown", joinDown);
+ bind("Mod-BracketLeft", lift);
+ bind("Escape", selectParentNode);
+
+ if (type = schema.marks.strong) {
+ bind("Mod-b", toggleMark(type));
+ bind("Mod-B", toggleMark(type));
+ }
+ if (type = schema.marks.em) {
+ bind("Mod-i", toggleMark(type));
+ bind("Mod-I", toggleMark(type));
+ }
+ if (type = schema.marks.code) {
+ bind("Mod-`", toggleMark(type));
+ }
+
+ if (type = schema.nodes.bullet_list) {
+ bind("Shift-Ctrl-8", wrapInList(type));
+ }
+ if (type = schema.nodes.ordered_list) {
+ bind("Shift-Ctrl-9", wrapInList(type));
+ }
+ if (type = schema.nodes.blockquote) {
+ bind("Ctrl->", wrapIn(type));
+ }
+ if (type = schema.nodes.hard_break) {
+ let br = type, cmd = chainCommands(exitCode, (state, dispatch) => {
+ if (dispatch) {
+ dispatch(state.tr.replaceSelectionWith(br.create()).scrollIntoView());
+ return true;
+ }
+ return false;
+ });
+ bind("Mod-Enter", cmd);
+ bind("Shift-Enter", cmd);
+ if (mac) {
+ bind("Ctrl-Enter", cmd);
+ }
+ }
+ if (type = schema.nodes.list_item) {
+ bind("Enter", splitListItem(type));
+ bind("Mod-[", liftListItem(type));
+ bind("Mod-]", sinkListItem(type));
+ }
+ if (type = schema.nodes.paragraph) {
+ bind("Shift-Ctrl-0", setBlockType(type));
+ }
+ if (type = schema.nodes.code_block) {
+ bind("Shift-Ctrl-\\", setBlockType(type));
+ }
+ if (type = schema.nodes.heading) {
+ for (let i = 1; i <= 6; i++) {
+ bind("Shift-Ctrl-" + i, setBlockType(type, { level: i }));
+ }
+ }
+ if (type = schema.nodes.horizontal_rule) {
+ let hr = type;
+ bind("Mod-_", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
+ return true;
+ });
+ }
+
+ return keys;
+} \ No newline at end of file
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 489c22a57..290e26dc3 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,141 +1,141 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model"
-import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands'
-import { redo, undo } from 'prosemirror-history'
-import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'
+import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model";
+import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands';
+import { redo, undo } from 'prosemirror-history';
+import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list';
import { EditorState, Transaction, NodeSelection, } from "prosemirror-state";
import { EditorView, } from "prosemirror-view";
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
- preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]
+ preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
// :: Object
// [Specs](#model.NodeSpec) for the nodes defined in this schema.
export const nodes: { [index: string]: NodeSpec } = {
- // :: NodeSpec The top level document node.
- doc: {
- content: "block+"
- },
-
- // :: NodeSpec A plain paragraph textblock. Represented in the DOM
- // as a `<p>` element.
- paragraph: {
- content: "inline*",
- group: "block",
- parseDOM: [{ tag: "p" }],
- toDOM() { return pDOM }
- },
-
- // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
- blockquote: {
- content: "block+",
- group: "block",
- defining: true,
- parseDOM: [{ tag: "blockquote" }],
- toDOM() { return blockquoteDOM }
- },
-
- // :: NodeSpec A horizontal rule (`<hr>`).
- horizontal_rule: {
- group: "block",
- parseDOM: [{ tag: "hr" }],
- toDOM() { return hrDOM }
- },
-
- // :: NodeSpec A heading textblock, with a `level` attribute that
- // should hold the number 1 to 6. Parsed and serialized as `<h1>` to
- // `<h6>` elements.
- heading: {
- attrs: { level: { default: 1 } },
- content: "inline*",
- group: "block",
- defining: true,
- parseDOM: [{ tag: "h1", attrs: { level: 1 } },
- { tag: "h2", attrs: { level: 2 } },
- { tag: "h3", attrs: { level: 3 } },
- { tag: "h4", attrs: { level: 4 } },
- { tag: "h5", attrs: { level: 5 } },
- { tag: "h6", attrs: { level: 6 } }],
- toDOM(node: any) { return ["h" + node.attrs.level, 0] }
- },
-
- // :: NodeSpec A code listing. Disallows marks or non-text inline
- // nodes by default. Represented as a `<pre>` element with a
- // `<code>` element inside of it.
- code_block: {
- content: "text*",
- marks: "",
- group: "block",
- code: true,
- defining: true,
- parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
- toDOM() { return preDOM }
- },
-
- // :: NodeSpec The text node.
- text: {
- group: "inline"
- },
-
- // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
- // `alt`, and `href` attributes. The latter two default to the empty
- // string.
- image: {
- inline: true,
- attrs: {
- src: {},
- alt: { default: null },
- title: { default: null }
- },
- group: "inline",
- draggable: true,
- parseDOM: [{
- tag: "img[src]", getAttrs(dom: any) {
- return {
- src: dom.getAttribute("src"),
- title: dom.getAttribute("title"),
- alt: dom.getAttribute("alt")
- }
- }
- }],
- toDOM(node: any) { return ["img", node.attrs] }
- },
-
- // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
- hard_break: {
- inline: true,
- group: "inline",
- selectable: false,
- parseDOM: [{ tag: "br" }],
- toDOM() { return brDOM }
- },
-
- ordered_list: {
- ...orderedList,
- content: 'list_item+',
- group: 'block'
- },
- //this doesn't currently work for some reason
- bullet_list: {
- ...bulletList,
- content: 'list_item+',
- group: 'block',
- // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }],
- // toDOM() { return ulDOM }
- },
- //bullet_list: {
- // content: 'list_item+',
- // group: 'block',
- //active: blockActive(schema.nodes.bullet_list),
- //enable: wrapInList(schema.nodes.bullet_list),
- //run: wrapInList(schema.nodes.bullet_list),
- //select: state => true,
- // },
- list_item: {
- ...listItem,
- content: 'paragraph block*'
- }
-}
+ // :: NodeSpec The top level document node.
+ doc: {
+ content: "block+"
+ },
+
+ // :: NodeSpec A plain paragraph textblock. Represented in the DOM
+ // as a `<p>` element.
+ paragraph: {
+ content: "inline*",
+ group: "block",
+ parseDOM: [{ tag: "p" }],
+ toDOM() { return pDOM; }
+ },
+
+ // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
+ blockquote: {
+ content: "block+",
+ group: "block",
+ defining: true,
+ parseDOM: [{ tag: "blockquote" }],
+ toDOM() { return blockquoteDOM; }
+ },
+
+ // :: NodeSpec A horizontal rule (`<hr>`).
+ horizontal_rule: {
+ group: "block",
+ parseDOM: [{ tag: "hr" }],
+ toDOM() { return hrDOM; }
+ },
+
+ // :: NodeSpec A heading textblock, with a `level` attribute that
+ // should hold the number 1 to 6. Parsed and serialized as `<h1>` to
+ // `<h6>` elements.
+ heading: {
+ attrs: { level: { default: 1 } },
+ content: "inline*",
+ group: "block",
+ defining: true,
+ parseDOM: [{ tag: "h1", attrs: { level: 1 } },
+ { tag: "h2", attrs: { level: 2 } },
+ { tag: "h3", attrs: { level: 3 } },
+ { tag: "h4", attrs: { level: 4 } },
+ { tag: "h5", attrs: { level: 5 } },
+ { tag: "h6", attrs: { level: 6 } }],
+ toDOM(node: any) { return ["h" + node.attrs.level, 0]; }
+ },
+
+ // :: NodeSpec A code listing. Disallows marks or non-text inline
+ // nodes by default. Represented as a `<pre>` element with a
+ // `<code>` element inside of it.
+ code_block: {
+ content: "text*",
+ marks: "",
+ group: "block",
+ code: true,
+ defining: true,
+ parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
+ toDOM() { return preDOM; }
+ },
+
+ // :: NodeSpec The text node.
+ text: {
+ group: "inline"
+ },
+
+ // :: NodeSpec An inline image (`<img>`) node. Supports `src`,
+ // `alt`, and `href` attributes. The latter two default to the empty
+ // string.
+ image: {
+ inline: true,
+ attrs: {
+ src: {},
+ alt: { default: null },
+ title: { default: null }
+ },
+ group: "inline",
+ draggable: true,
+ parseDOM: [{
+ tag: "img[src]", getAttrs(dom: any) {
+ return {
+ src: dom.getAttribute("src"),
+ title: dom.getAttribute("title"),
+ alt: dom.getAttribute("alt")
+ };
+ }
+ }],
+ toDOM(node: any) { return ["img", node.attrs]; }
+ },
+
+ // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
+ hard_break: {
+ inline: true,
+ group: "inline",
+ selectable: false,
+ parseDOM: [{ tag: "br" }],
+ toDOM() { return brDOM; }
+ },
+
+ ordered_list: {
+ ...orderedList,
+ content: 'list_item+',
+ group: 'block'
+ },
+ //this doesn't currently work for some reason
+ bullet_list: {
+ ...bulletList,
+ content: 'list_item+',
+ group: 'block',
+ // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }],
+ // toDOM() { return ulDOM }
+ },
+ //bullet_list: {
+ // content: 'list_item+',
+ // group: 'block',
+ //active: blockActive(schema.nodes.bullet_list),
+ //enable: wrapInList(schema.nodes.bullet_list),
+ //run: wrapInList(schema.nodes.bullet_list),
+ //select: state => true,
+ // },
+ list_item: {
+ ...listItem,
+ content: 'paragraph block*'
+ }
+};
const emDOM: DOMOutputSpecArray = ["em", 0];
const strongDOM: DOMOutputSpecArray = ["strong", 0];
@@ -144,93 +144,93 @@ const underlineDOM: DOMOutputSpecArray = ["underline", 0];
// :: Object [Specs](#model.MarkSpec) for the marks in the schema.
export const marks: { [index: string]: MarkSpec } = {
- // :: MarkSpec A link. Has `href` and `title` attributes. `title`
- // defaults to the empty string. Rendered and parsed as an `<a>`
- // element.
- link: {
- attrs: {
- href: {},
- title: { default: null }
- },
- inclusive: false,
- parseDOM: [{
- tag: "a[href]", getAttrs(dom: any) {
- return { href: dom.getAttribute("href"), title: dom.getAttribute("title") }
- }
- }],
- toDOM(node: any) { return ["a", node.attrs, 0] }
- },
-
- // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
- // Has parse rules that also match `<i>` and `font-style: italic`.
- em: {
- parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }],
- toDOM() { return emDOM }
- },
-
- // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
- // also match `<b>` and `font-weight: bold`.
- strong: {
- parseDOM: [{ tag: "strong" },
- { tag: "b" },
- { style: "font-weight" }],
- toDOM() { return strongDOM }
- },
-
- underline: {
- parseDOM: [
- { tag: 'u' },
- { style: 'text-decoration=underline' }
- ],
- toDOM: () => ['span', {
- style: 'text-decoration:underline'
- }]
- },
-
- strikethrough: {
- parseDOM: [
- { tag: 'strike' },
- { style: 'text-decoration=line-through' },
- { style: 'text-decoration-line=line-through' }
- ],
- toDOM: () => ['span', {
- style: 'text-decoration-line:line-through'
- }]
- },
-
- subscript: {
- excludes: 'superscript',
- parseDOM: [
- { tag: 'sub' },
- { style: 'vertical-align=sub' }
- ],
- toDOM: () => ['sub']
- },
-
- superscript: {
- excludes: 'subscript',
- parseDOM: [
- { tag: 'sup' },
- { style: 'vertical-align=super' }
- ],
- toDOM: () => ['sup']
- },
-
-
- // :: MarkSpec Code font mark. Represented as a `<code>` element.
- code: {
- parseDOM: [{ tag: "code" }],
- toDOM() { return codeDOM }
- },
-
-
- timesNewRoman: {
- parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: "Times New Roman", Times, serif;'
- }]
- },
-}
+ // :: MarkSpec A link. Has `href` and `title` attributes. `title`
+ // defaults to the empty string. Rendered and parsed as an `<a>`
+ // element.
+ link: {
+ attrs: {
+ href: {},
+ title: { default: null }
+ },
+ inclusive: false,
+ parseDOM: [{
+ tag: "a[href]", getAttrs(dom: any) {
+ return { href: dom.getAttribute("href"), title: dom.getAttribute("title") };
+ }
+ }],
+ toDOM(node: any) { return ["a", node.attrs, 0]; }
+ },
+
+ // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
+ // Has parse rules that also match `<i>` and `font-style: italic`.
+ em: {
+ parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }],
+ toDOM() { return emDOM; }
+ },
+
+ // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
+ // also match `<b>` and `font-weight: bold`.
+ strong: {
+ parseDOM: [{ tag: "strong" },
+ { tag: "b" },
+ { style: "font-weight" }],
+ toDOM() { return strongDOM; }
+ },
+
+ underline: {
+ parseDOM: [
+ { tag: 'u' },
+ { style: 'text-decoration=underline' }
+ ],
+ toDOM: () => ['span', {
+ style: 'text-decoration:underline'
+ }]
+ },
+
+ strikethrough: {
+ parseDOM: [
+ { tag: 'strike' },
+ { style: 'text-decoration=line-through' },
+ { style: 'text-decoration-line=line-through' }
+ ],
+ toDOM: () => ['span', {
+ style: 'text-decoration-line:line-through'
+ }]
+ },
+
+ subscript: {
+ excludes: 'superscript',
+ parseDOM: [
+ { tag: 'sub' },
+ { style: 'vertical-align=sub' }
+ ],
+ toDOM: () => ['sub']
+ },
+
+ superscript: {
+ excludes: 'subscript',
+ parseDOM: [
+ { tag: 'sup' },
+ { style: 'vertical-align=super' }
+ ],
+ toDOM: () => ['sup']
+ },
+
+
+ // :: MarkSpec Code font mark. Represented as a `<code>` element.
+ code: {
+ parseDOM: [{ tag: "code" }],
+ toDOM() { return codeDOM; }
+ },
+
+
+ timesNewRoman: {
+ parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }],
+ toDOM: () => ['span', {
+ style: 'font-family: "Times New Roman", Times, serif;'
+ }]
+ },
+};
// :: Schema
// This schema rougly corresponds to the document schema used by
@@ -240,4 +240,4 @@ export const marks: { [index: string]: MarkSpec } = {
//
// To reuse elements from this schema, extend or read from its
// `spec.nodes` and `spec.marks` [properties](#model.Schema.spec).
-export const schema = new Schema({ nodes, marks }) \ No newline at end of file
+export const schema = new Schema({ nodes, marks }); \ No newline at end of file
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 7c0649a6a..9015f21cf 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -14,7 +14,7 @@ import { ListField } from "../../fields/ListField";
// import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
// @ts-ignore
-import * as typescriptlib from '!!raw-loader!./type_decls.d'
+import * as typescriptlib from '!!raw-loader!./type_decls.d';
import { Documents } from "../documents/Documents";
import { Key } from "../../fields/Key";
@@ -30,8 +30,10 @@ export interface ScriptError {
export type ScriptResult = ScriptSucccess | ScriptError;
-export interface CompileSuccess {
- compiled: true;
+export interface CompiledScript {
+ readonly compiled: true;
+ readonly originalScript: string;
+ readonly options: Readonly<ScriptOptions>;
run(args?: { [name: string]: any }): ScriptResult;
}
@@ -40,17 +42,17 @@ export interface CompileError {
errors: any[];
}
-export type CompiledScript = CompileSuccess | CompileError;
+export type CompileResult = CompiledScript | CompileError;
-function Run(script: string | undefined, customParams: string[], diagnostics: any[]): CompiledScript {
- const errors = diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error);
+function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
+ const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error);
if (errors || !script) {
return { compiled: false, errors: diagnostics };
}
let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key];
let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)];
- let params: any[] = [KeyStore, Documents, ...fieldTypes]
+ let params: any[] = [KeyStore, Documents, ...fieldTypes];
let compiledFunction = new Function(...paramNames, `return ${script}`);
let run = (args: { [name: string]: any } = {}): ScriptResult => {
let argsArray: any[] = [];
@@ -60,15 +62,15 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
}
argsArray.push(args[name]);
}
- let thisParam = args["this"];
+ let thisParam = args.this;
try {
const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
return { success: true, result };
} catch (error) {
return { success: false, error };
}
- }
- return { compiled: true, run };
+ };
+ return { compiled: true, run, originalScript, options };
}
interface File {
@@ -90,14 +92,14 @@ class ScriptingCompilerHost {
}
// getDefaultLibFileName(options: ts.CompilerOptions): string {
getDefaultLibFileName(options: any): string {
- return 'node_modules/typescript/lib/lib.d.ts' // No idea what this means...
+ return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means...
}
writeFile(fileName: string, content: string) {
const file = this.files.find(file => file.fileName === fileName);
if (file) {
file.content = content;
} else {
- this.files.push({ fileName, content })
+ this.files.push({ fileName, content });
}
}
getCurrentDirectory(): string {
@@ -130,7 +132,7 @@ export interface ScriptOptions {
params?: { [name: string]: string };
}
-export function CompileScript(script: string, { requiredType = "", addReturn = false, params = {} }: ScriptOptions = {}): CompiledScript {
+export function CompileScript(script: string, { requiredType = "", addReturn = false, params = {} }: ScriptOptions = {}): CompileResult {
let host = new ScriptingCompilerHost;
let paramArray: string[] = [];
if ("this" in params) {
@@ -140,7 +142,10 @@ export function CompileScript(script: string, { requiredType = "", addReturn = f
if (key === "this") continue;
paramArray.push(key);
}
- let paramString = paramArray.map(key => `${key}: ${params[key]}`).join(", ");
+ let paramString = paramArray.map(key => {
+ const val = params[key];
+ return `${key}: ${val}`;
+ }).join(", ");
let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
${addReturn ? `return ${script};` : script}
})`;
@@ -152,7 +157,7 @@ export function CompileScript(script: string, { requiredType = "", addReturn = f
let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
- return Run(outputText, paramArray, diagnostics);
+ return Run(outputText, paramArray, diagnostics, script, { requiredType, addReturn, params });
}
export function OrLiteralType(returnType: string): string {
@@ -160,9 +165,9 @@ export function OrLiteralType(returnType: string): string {
}
export function ToField(data: any): Opt<Field> {
- if (typeof data == "string") {
+ if (typeof data === "string") {
return new TextField(data);
- } else if (typeof data == "number") {
+ } else if (typeof data === "number") {
return new NumberField(data);
}
return undefined;
diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
index b6b088170..a209874a3 100644
--- a/src/client/util/ScrollBox.tsx
+++ b/src/client/util/ScrollBox.tsx
@@ -1,4 +1,4 @@
-import React = require("react")
+import React = require("react");
export class ScrollBox extends React.Component {
onWheel = (e: React.WheelEvent) => {
@@ -16,6 +16,6 @@ export class ScrollBox extends React.Component {
}} onWheel={this.onWheel}>
{this.props.children}
</div>
- )
+ );
}
} \ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 79d4ceb25..5ddaafc72 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -12,13 +12,20 @@ export namespace SelectionManager {
SelectDoc(doc: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
if (!ctrlPressed) {
- manager.SelectedDocuments = [];
+ this.DeselectAll();
}
if (manager.SelectedDocuments.indexOf(doc) === -1) {
manager.SelectedDocuments.push(doc);
+ doc.props.onActiveChanged(true);
}
}
+
+ @action
+ DeselectAll(): void {
+ manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false));
+ manager.SelectedDocuments = [];
+ }
}
const manager = new Manager();
@@ -34,13 +41,13 @@ export namespace SelectionManager {
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;
+ for (const view of manager.SelectedDocuments) {
+ if (view.props.Document === except) found = view;
}
}
- manager.SelectedDocuments.length = 0;
- if (found) manager.SelectedDocuments.push(found);
+
+ manager.DeselectAll();
+ if (found) manager.SelectDoc(found, false);
Main.Instance.SetTextDoc(undefined, undefined);
}
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 82e9d1bac..48f835a0f 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -10,14 +10,12 @@ import { Schema, NodeType } from "prosemirror-model";
import React = require("react");
import "./TooltipTextMenu.scss";
const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
-import { library } from '@fortawesome/fontawesome-svg-core'
-import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list'
-import {
- faListUl,
-} from '@fortawesome/free-solid-svg-icons';
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list';
+import { faListUl } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-const SVG = "http://www.w3.org/2000/svg"
+const SVG = "http://www.w3.org/2000/svg";
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
@@ -46,7 +44,7 @@ export class TooltipTextMenu {
{ command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
{ command: toggleMark(schema.marks.timesNewRoman), dom: this.icon("x", "TNR") },
{ command: lift, dom: this.icon("<", "lift") },
- ]
+ ];
//add menu items
items.forEach(({ dom, command }) => {
this.tooltip.appendChild(dom);
@@ -67,8 +65,8 @@ export class TooltipTextMenu {
//uncomment this if we want the bullet button to disappear if current selection is bulleted
//dom.style.display = active ? "" : "none";
}
- })
- })
+ });
+ });
this.update(view, undefined);
}
@@ -129,32 +127,32 @@ export class TooltipTextMenu {
return {
command: setBlockType(schema.nodes.heading, { level }),
dom: this.icon("H" + level, "heading")
- }
+ };
}
//updates the tooltip menu when the selection changes
update(view: EditorView, lastState: EditorState | undefined) {
- let state = view.state
+ let state = view.state;
// Don't do anything if the document/selection didn't change
if (lastState && lastState.doc.eq(state.doc) &&
- lastState.selection.eq(state.selection)) return
+ lastState.selection.eq(state.selection)) return;
// Hide the tooltip if the selection is empty
if (state.selection.empty) {
- this.tooltip.style.display = "none"
- return
+ this.tooltip.style.display = "none";
+ return;
}
// Otherwise, reposition it and update its content
- this.tooltip.style.display = ""
- let { from, to } = state.selection
- let start = view.coordsAtPos(from), end = view.coordsAtPos(to)
+ this.tooltip.style.display = "";
+ let { from, to } = state.selection;
+ let start = view.coordsAtPos(from), end = view.coordsAtPos(to);
// The box in which the tooltip is positioned, to use as base
- let box = this.tooltip.offsetParent!.getBoundingClientRect()
+ let box = this.tooltip.offsetParent!.getBoundingClientRect();
// Find a center-ish x position from the selection endpoints (when
// crossing lines, end may be more to the left)
- let left = Math.max((start.left + end.left) / 2, start.left + 3)
- this.tooltip.style.left = (left - box.left) + "px"
+ let left = Math.max((start.left + end.left) / 2, start.left + 3);
+ this.tooltip.style.left = (left - box.left) + "px";
//let width = Math.abs(start.left - end.left) / 2;
let width = 8 * 16 + 15;
let mid = Math.min(start.left, end.left) + width;
@@ -164,5 +162,5 @@ export class TooltipTextMenu {
this.tooltip.style.bottom = (box.bottom - start.top) + "px";
}
- destroy() { this.tooltip.remove() }
+ destroy() { this.tooltip.remove(); }
}
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index 060c5da82..e9170ec36 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -3,7 +3,7 @@ export class Transform {
private _translateY: number = 0;
private _scale: number = 1;
- static get Identity(): Transform {
+ static Identity(): Transform {
return new Transform(0, 0, 1);
}
@@ -62,33 +62,19 @@ export class Transform {
return this;
}
- translated = (x: number, y: number): Transform => {
- return this.copy().translate(x, y);
- }
+ translated = (x: number, y: number): Transform => this.copy().translate(x, y);
- preTranslated = (x: number, y: number): Transform => {
- return this.copy().preTranslate(x, y);
- }
+ preTranslated = (x: number, y: number): Transform => this.copy().preTranslate(x, y);
- scaled = (scale: number): Transform => {
- return this.copy().scale(scale);
- }
+ scaled = (scale: number): Transform => this.copy().scale(scale);
- scaledAbout = (scale: number, x: number, y: number): Transform => {
- return this.copy().scaleAbout(scale, x, y);
- }
+ scaledAbout = (scale: number, x: number, y: number): Transform => this.copy().scaleAbout(scale, x, y);
- preScaled = (scale: number): Transform => {
- return this.copy().preScale(scale);
- }
+ preScaled = (scale: number): Transform => this.copy().preScale(scale);
- transformed = (transform: Transform): Transform => {
- return this.copy().transform(transform);
- }
+ transformed = (transform: Transform): Transform => this.copy().transform(transform);
- preTransformed = (transform: Transform): Transform => {
- return this.copy().preTransform(transform);
- }
+ preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform);
transformPoint = (x: number, y: number): [number, number] => {
x *= this._scale;
@@ -98,9 +84,7 @@ export class Transform {
return [x, y];
}
- transformDirection = (x: number, y: number): [number, number] => {
- return [x * this._scale, y * this._scale];
- }
+ transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale];
transformBounds(x: number, y: number, width: number, height: number): { x: number, y: number, width: number, height: number } {
[x, y] = this.transformPoint(x, y);
@@ -108,12 +92,8 @@ export class Transform {
return { x, y, width, height };
}
- inverse = () => {
- return new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale)
- }
+ inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale);
- copy = () => {
- return new Transform(this._translateX, this._translateY, this._scale);
- }
+ copy = () => new Transform(this._translateX, this._translateY, this._scale);
} \ No newline at end of file
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 0714a7f5c..532ba78eb 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -36,7 +36,5 @@ export class TypedEvent<T> {
this.listenersOncer = [];
}
- pipe = (te: TypedEvent<T>): Disposable => {
- return this.on((e) => te.emit(e));
- }
+ pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e));
} \ No newline at end of file
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index eb13ff1ee..27aed4bac 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,5 +1,5 @@
import { observable, action } from "mobx";
-import 'source-map-support/register'
+import 'source-map-support/register';
import { Without } from "../../Utils";
import { string } from "prop-types";
@@ -31,11 +31,24 @@ function propertyDecorator(target: any, key: string | symbol) {
batch.end();
}
}
- })
+ });
}
- })
+ });
}
-export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
+
+export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any;
+export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any;
+export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
+ if (!key) {
+ return function () {
+ let batch = UndoManager.StartBatch("");
+ try {
+ return target.apply(undefined, arguments);
+ } finally {
+ batch.end();
+ }
+ };
+ }
if (!descriptor) {
propertyDecorator(target, key);
return;
@@ -45,11 +58,11 @@ export function undoBatch(target: any, key: string | symbol, descriptor?: TypedP
descriptor.value = function (...args: any[]) {
let batch = UndoManager.StartBatch(getBatchName(target, key));
try {
- return oldFunction.apply(this, args)
+ return oldFunction.apply(this, args);
} finally {
batch.end();
}
- }
+ };
return descriptor;
}
@@ -104,8 +117,8 @@ export namespace UndoManager {
EndBatch(cancel);
}
- end = () => { this.dispose(false); }
- cancel = () => { this.dispose(true); }
+ end = () => { this.dispose(false); };
+ cancel = () => { this.dispose(true); };
}
export function StartBatch(batchName: string): Batch {
@@ -125,12 +138,15 @@ export namespace UndoManager {
redoStack.length = 0;
currentBatch = undefined;
}
- })
+ });
export function RunInBatch(fn: () => void, batchName: string) {
let batch = StartBatch(batchName);
- fn();
- batch.end();
+ try {
+ fn();
+ } finally {
+ batch.end();
+ }
}
export const Undo = action(() => {
@@ -150,7 +166,7 @@ export namespace UndoManager {
undoing = false;
redoStack.push(commands);
- })
+ });
export const Redo = action(() => {
if (redoStack.length === 0) {
@@ -163,12 +179,12 @@ export namespace UndoManager {
}
undoing = true;
- for (let i = 0; i < commands.length; i++) {
- commands[i].redo();
+ for (const command of commands) {
+ command.redo();
}
undoing = false;
undoStack.push(commands);
- })
+ });
} \ No newline at end of file
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 4f69053b1..47c3481b2 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -181,7 +181,7 @@ declare class Key extends Field {
Copy(): Field;
ToScriptString(): string;
}
-declare type FIELD_WAITING = "<Waiting>";
+declare type FIELD_WAITING = null;
declare type Opt<T> = T | undefined;
declare type FieldValue<T> = Opt<T> | FIELD_WAITING;
// @ts-ignore