aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/DocumentManager.ts6
-rw-r--r--src/client/util/History.ts2
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx4
-rw-r--r--src/client/util/Scripting.ts69
-rw-r--r--src/client/util/SearchUtil.ts40
-rw-r--r--src/client/util/TooltipTextMenu.tsx13
6 files changed, 108 insertions, 26 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index bb1345044..262194a40 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -3,7 +3,7 @@ import { DocumentView } from '../views/nodes/DocumentView';
import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
import { FieldValue, Cast, NumCast, BoolCast, StrCast } from '../../new_fields/Types';
import { listSpec } from '../../new_fields/Schema';
-import { undoBatch } from './UndoManager';
+import { undoBatch, UndoManager } from './UndoManager';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { CollectionView } from '../views/collections/CollectionView';
import { CollectionPDFView } from '../views/collections/CollectionPDFView';
@@ -142,7 +142,9 @@ export class DocumentManager {
if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) {
docView.props.Document.libraryBrush = true;
if (linkPage !== undefined) docView.props.Document.curPage = linkPage;
- docView.props.focus(docView.props.Document, willZoom);
+ UndoManager.RunInBatch(() => {
+ docView!.props.focus(docView!.props.Document, willZoom);
+ }, "focus");
} else {
if (!contextDoc) {
if (docContext) {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index cbf5b3fc8..e9ff21b22 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -129,7 +129,7 @@ export namespace HistoryUtil {
function addStringifier(type: string, keys: string[], customStringifier?: (state: ParsedUrl, current: string) => string) {
stringifiers[type] = state => {
- let path = DocServer.prepend(`/${type}`);
+ let path = Utils.prepend(`/${type}`);
if (customStringifier) {
path = customStringifier(state, path);
}
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index c096e9ceb..75b0b52a7 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -98,12 +98,12 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
runInAction(() => this.remaining++);
- let prom = fetch(DocServer.prepend(RouteStore.upload), {
+ let prom = fetch(Utils.prepend(RouteStore.upload), {
method: 'POST',
body: formData
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
- let docPromise = Docs.Get.DocumentFromType(type, DocServer.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName });
+ let docPromise = Docs.Get.DocumentFromType(type, Utils.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName });
docPromise.then(doc => {
doc && docs.push(doc) && runInAction(() => this.remaining--);
});
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 62c2cfe85..46dc320b0 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,5 +1,7 @@
-// import * as ts from "typescript"
-let ts = (window as any).ts;
+import * as ts from "typescript";
+export { ts };
+// export const ts = (window as any).ts;
+
// // @ts-ignore
// import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts'
// // @ts-ignore
@@ -55,13 +57,35 @@ export namespace Scripting {
}
scriptingGlobals[n] = obj;
}
+
+ export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) {
+ return { ..._scriptingGlobals, ...(globals || {}) };
+ }
+
+ export function setScriptingGlobals(globals: { [key: string]: any }) {
+ scriptingGlobals = globals;
+ }
+
+ export function resetScriptingGlobals() {
+ scriptingGlobals = _scriptingGlobals;
+ }
+
+ // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]);
+ export function printNodeType(node: any, indentation = "") {
+ console.log(indentation + ts.SyntaxKind[node.kind]);
+ }
+
+ export function getGlobals() {
+ return Object.keys(scriptingGlobals);
+ }
}
export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
Scripting.addGlobal(constructor);
}
-const scriptingGlobals: { [name: string]: any } = {};
+const _scriptingGlobals: { [name: string]: any } = {};
+let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error);
@@ -162,6 +186,8 @@ class ScriptingCompilerHost {
}
}
+export type Traverser = (node: ts.Node, indentation: string) => boolean | void;
+export type TraverserParam = Traverser | { onEnter: Traverser, onLeave: Traverser };
export interface ScriptOptions {
requiredType?: string;
addReturn?: boolean;
@@ -169,10 +195,23 @@ export interface ScriptOptions {
capturedVariables?: { [name: string]: Field };
typecheck?: boolean;
editable?: boolean;
+ traverser?: TraverserParam;
+ transformer?: ts.TransformerFactory<ts.SourceFile>;
+ globals?: { [name: string]: any };
+}
+
+// function forEachNode(node:ts.Node, fn:(node:any) => void);
+function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, indentation = "") {
+ return onEnter(node, indentation) || ts.forEachChild(node, (n: any) => {
+ forEachNode(n, onEnter, onExit, indentation + " ");
+ }) || (onExit && onExit(node, indentation));
}
export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult {
const { requiredType = "", addReturn = false, params = {}, capturedVariables = {}, typecheck = true } = options;
+ if (options.globals) {
+ Scripting.setScriptingGlobals(options.globals);
+ }
let host = new ScriptingCompilerHost;
let paramNames: string[] = [];
if ("this" in params || "this" in capturedVariables) {
@@ -192,10 +231,27 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
paramList.push(`${key}: ${capturedVariables[key].constructor.name}`);
}
let paramString = paramList.join(", ");
+ if (options.traverser) {
+ const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true);
+ const onEnter = typeof options.traverser === "object" ? options.traverser.onEnter : options.traverser;
+ const onLeave = typeof options.traverser === "object" ? options.traverser.onLeave : undefined;
+ forEachNode(sourceFile, onEnter, onLeave);
+ }
+ if (options.transformer) {
+ const sourceFile = ts.createSourceFile('script.ts', script, ts.ScriptTarget.ES2015, true);
+ const result = ts.transform(sourceFile, [options.transformer]);
+ const transformed = result.transformed;
+ const printer = ts.createPrinter({
+ newLine: ts.NewLineKind.LineFeed
+ });
+ script = printer.printFile(transformed[0]);
+ result.dispose();
+ }
let funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
${addReturn ? `return ${script};` : script}
})`;
host.writeFile("file.ts", funcScript);
+
if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
let program = ts.createProgram(["file.ts"], {}, host);
let testResult = program.emit();
@@ -203,7 +259,12 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
- return Run(outputText, paramNames, diagnostics, script, options);
+ const result = Run(outputText, paramNames, diagnostics, script, options);
+
+ if (options.globals) {
+ Scripting.resetScriptingGlobals();
+ }
+ return result;
}
Scripting.addGlobal(CompileScript); \ No newline at end of file
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 806746496..ee5a83710 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -2,32 +2,44 @@ import * as rp from 'request-promise';
import { DocServer } from '../DocServer';
import { Doc } from '../../new_fields/Doc';
import { Id } from '../../new_fields/FieldSymbols';
+import { Utils } from '../../Utils';
export namespace SearchUtil {
+ export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
+
export interface IdSearchResult {
ids: string[];
numFound: number;
+ highlighting: HighlightingResult | undefined;
}
export interface DocSearchResult {
docs: Doc[];
numFound: number;
+ highlighting: HighlightingResult | undefined;
}
- export function Search(query: string, filterQuery: string | undefined, returnDocs: true, start?: number, count?: number): Promise<DocSearchResult>;
- export function Search(query: string, filterQuery: string | undefined, returnDocs: false, start?: number, count?: number): Promise<IdSearchResult>;
- export async function Search(query: string, filterQuery: string | undefined, returnDocs: boolean, start?: number, rows?: number) {
+ export interface SearchParams {
+ hl?: boolean;
+ "hl.fl"?: string;
+ start?: number;
+ rows?: number;
+ fq?: string;
+ }
+ export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
+ export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
+ export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
query = query || "*"; //If we just have a filter query, search for * as the query
- const result: IdSearchResult = JSON.parse(await rp.get(DocServer.prepend("/search"), {
- qs: { query, filterQuery, start, rows },
+ const result: IdSearchResult = JSON.parse(await rp.get(Utils.prepend("/search"), {
+ qs: { ...options, q: query },
}));
if (!returnDocs) {
return result;
}
- const { ids, numFound } = result;
+ const { ids, numFound, highlighting } = result;
const docMap = await DocServer.GetRefFields(ids);
const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc);
- return { docs, numFound };
+ return { docs, numFound, highlighting };
}
export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
@@ -36,31 +48,31 @@ export namespace SearchUtil {
const proto = Doc.GetProto(doc);
const protoId = proto[Id];
if (returnDocs) {
- return (await Search("", `proto_i:"${protoId}"`, returnDocs)).docs;
+ return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).docs;
} else {
- return (await Search("", `proto_i:"${protoId}"`, returnDocs)).ids;
+ return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).ids;
}
// return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
}
export async function GetViewsOfDocument(doc: Doc): Promise<Doc[]> {
- const results = await Search("", `proto_i:"${doc[Id]}"`, true);
+ const results = await Search("", true, { fq: `proto_i:"${doc[Id]}"` });
return results.docs;
}
export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[], aliasContexts: Doc[] }> {
- const docContexts = (await Search("", `data_l:"${doc[Id]}"`, true)).docs;
+ const docContexts = (await Search("", true, { fq: `data_l:"${doc[Id]}"` })).docs;
const aliases = await GetAliasesOfDocument(doc, false);
- const aliasContexts = (await Promise.all(aliases.map(doc => Search("", `data_l:"${doc}"`, true))));
+ const aliasContexts = (await Promise.all(aliases.map(doc => Search("", true, { fq: `data_l:"${doc}"` }))));
const contexts = { contexts: docContexts, aliasContexts: [] as Doc[] };
aliasContexts.forEach(result => contexts.aliasContexts.push(...result.docs));
return contexts;
}
export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[], aliasContexts: string[] }> {
- const docContexts = (await Search("", `data_l:"${doc[Id]}"`, false)).ids;
+ const docContexts = (await Search("", false, { fq: `data_l:"${doc[Id]}"` })).ids;
const aliases = await GetAliasesOfDocument(doc, false);
- const aliasContexts = (await Promise.all(aliases.map(doc => Search("", `data_l:"${doc}"`, false))));
+ const aliasContexts = (await Promise.all(aliases.map(doc => Search("", false, { fq: `data_l:"${doc}"` }))));
const contexts = { contexts: docContexts, aliasContexts: [] as string[] };
aliasContexts.forEach(result => contexts.aliasContexts.push(...result.ids));
return contexts;
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index f4579fc51..a4c053de2 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -19,6 +19,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie
import { DocumentManager } from "./DocumentManager";
import { Id } from "../../new_fields/FieldSymbols";
import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
+import { Utils } from "../../Utils";
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
@@ -212,8 +213,8 @@ export class TooltipTextMenu {
let link = node && node.marks.find(m => m.type.name === "link");
if (link) {
let href: string = link.attrs.href;
- if (href.indexOf(DocServer.prepend("/doc/")) === 0) {
- let docid = href.replace(DocServer.prepend("/doc/"), "");
+ if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ let docid = href.replace(Utils.prepend("/doc/"), "");
DocServer.GetRefField(docid).then(action((f: Opt<Field>) => {
if (f instanceof Doc) {
if (DocumentManager.Instance.getDocumentView(f)) {
@@ -239,6 +240,8 @@ export class TooltipTextMenu {
this.linkDrag.onpointerdown = (e: PointerEvent) => {
let dragData = new DragManager.LinkDragData(this.editorProps.Document);
dragData.dontClearTextBox = true;
+ // hack to get source context -sy
+ let docView = DocumentManager.Instance.getDocumentView(this.editorProps.Document);
e.stopPropagation();
let ctrlKey = e.ctrlKey;
DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY,
@@ -247,7 +250,11 @@ export class TooltipTextMenu {
dragComplete: action(() => {
// let m = dragData.droppedDocuments;
let linkDoc = dragData.linkDocument;
- linkDoc instanceof Doc && this.makeLink(DocServer.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab");
+ let proto = Doc.GetProto(linkDoc);
+ if (docView && docView.props.ContainingCollectionView) {
+ proto.sourceContext = docView.props.ContainingCollectionView.props.Document;
+ }
+ linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab");
}),
},
hideSource: false