diff options
Diffstat (limited to 'src/client/util')
| -rw-r--r-- | src/client/util/DocumentManager.ts | 16 | ||||
| -rw-r--r-- | src/client/util/DragManager.ts | 44 | ||||
| -rw-r--r-- | src/client/util/History.ts | 136 | ||||
| -rw-r--r-- | src/client/util/Import & Export/DirectoryImportBox.tsx | 18 | ||||
| -rw-r--r-- | src/client/util/LinkManager.ts | 11 | ||||
| -rw-r--r-- | src/client/util/RichTextSchema.tsx | 17 | ||||
| -rw-r--r-- | src/client/util/Scripting.ts | 72 | ||||
| -rw-r--r-- | src/client/util/SearchUtil.ts | 41 | ||||
| -rw-r--r-- | src/client/util/SerializationHelper.ts | 10 | ||||
| -rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 32 | ||||
| -rw-r--r-- | src/client/util/type_decls.d | 2 |
11 files changed, 302 insertions, 97 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index acd8dcef7..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'; @@ -106,14 +106,16 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { + let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush)).reduce((pairs, dv) => { let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document); pairs.push(...linksList.reduce((pairs, link) => { if (link) { let linkToDoc = LinkManager.Instance.getOppositeAnchor(link, dv.props.Document); - DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { - pairs.push({ a: dv, b: docView1, l: link }); - }); + if (linkToDoc) { + DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { + pairs.push({ a: dv, b: docView1, l: link }); + }); + } } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); @@ -140,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/DragManager.ts b/src/client/util/DragManager.ts index cb71db2c5..323908302 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -58,19 +58,27 @@ export function SetupDrag( return onItemDown; } +function moveLinkedDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + const document = SelectionManager.SelectedDocuments()[0]; + document.props.removeDocument && document.props.removeDocument(doc); + addDocument(doc); + return true; +} + export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) { let draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc); - - let moddrag = await Cast(draggeddoc.annotationOn, Doc); - let dragdocs = moddrag ? [moddrag] : [draggeddoc]; - let dragData = new DragManager.DocumentDragData(dragdocs, dragdocs); - dragData.dropAction = "alias" as dropActionType; - DragManager.StartLinkedDocumentDrag([dragEle], sourceDoc, dragData, x, y, { - handlers: { - dragComplete: action(emptyFunction), - }, - hideSource: false - }); + if (draggeddoc) { + let moddrag = await Cast(draggeddoc.annotationOn, Doc); + let dragdocs = moddrag ? [moddrag] : [draggeddoc]; + let dragData = new DragManager.DocumentDragData(dragdocs, dragdocs); + dragData.moveDocument = moveLinkedDocument; + DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } } export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { @@ -81,8 +89,9 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n let linkDocs = LinkManager.Instance.getAllRelatedLinks(srcTarg); if (linkDocs) { draggedDocs = linkDocs.map(link => { - return LinkManager.Instance.getOppositeAnchor(link, sourceDoc); - }); + let opp = LinkManager.Instance.getOppositeAnchor(link, sourceDoc); + if (opp) return opp; + }) as Doc[]; } } if (draggedDocs.length) { @@ -93,7 +102,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n } let dragdocs = moddrag.length ? moddrag : draggedDocs; let dragData = new DragManager.DocumentDragData(dragdocs, dragdocs); - DragManager.StartLinkedDocumentDrag([dragEle], sourceDoc, dragData, x, y, { + dragData.moveDocument = moveLinkedDocument; + DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), }, @@ -102,6 +112,7 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n } } + export namespace DragManager { export function Root() { const root = document.getElementById("root"); @@ -227,15 +238,14 @@ export namespace DragManager { }); } - export function StartLinkedDocumentDrag(eles: HTMLElement[], sourceDoc: Doc, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + export function StartLinkedDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + dragData.moveDocument = moveLinkedDocument; runInAction(() => StartDragFunctions.map(func => func())); StartDrag(eles, dragData, downX, downY, options, (dropData: { [id: string]: any }) => { - // dropData.droppedDocuments = let droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => { let dvs = DocumentManager.Instance.getDocumentViews(d); - if (dvs.length) { let inContext = dvs.filter(dv => dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView); if (inContext.length) { diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 545ea8629..e9ff21b22 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -2,6 +2,8 @@ import { Doc, Opt, Field } from "../../new_fields/Doc"; import { DocServer } from "../DocServer"; import { RouteStore } from "../../server/RouteStore"; import { MainView } from "../views/MainView"; +import * as qs from 'query-string'; +import { Utils, OmitKeys } from "../../Utils"; export namespace HistoryUtil { export interface DocInitializerList { @@ -11,9 +13,11 @@ export namespace HistoryUtil { export interface DocUrl { type: "doc"; docId: string; - initializers: { + initializers?: { [docId: string]: DocInitializerList; }; + readonly?: boolean; + nro?: boolean; } export type ParsedUrl = DocUrl; @@ -21,7 +25,7 @@ export namespace HistoryUtil { // const handlers: ((state: ParsedUrl | null) => void)[] = []; function onHistory(e: PopStateEvent) { if (window.location.pathname !== RouteStore.home) { - const url = e.state as ParsedUrl || parseUrl(window.location.pathname); + const url = e.state as ParsedUrl || parseUrl(window.location); if (url) { switch (url.type) { case "doc": @@ -62,42 +66,111 @@ export namespace HistoryUtil { // } // } - export function parseUrl(pathname: string): ParsedUrl | undefined { - let pathnameSplit = pathname.split("/"); - if (pathnameSplit.length !== 2) { - return undefined; + const parsers: { [type: string]: (pathname: string[], opts: qs.ParsedQuery) => ParsedUrl | undefined } = {}; + const stringifiers: { [type: string]: (state: ParsedUrl) => string } = {}; + + type ParserValue = true | "none" | "json" | ((value: string) => any); + + type Parser = { + [key: string]: ParserValue + }; + + function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: qs.ParsedQuery, current: ParsedUrl) => ParsedUrl | null | undefined) { + function parse(parser: ParserValue, value: string | string[] | null | undefined) { + if (value === undefined || value === null) { + return value; + } + if (Array.isArray(value)) { + } else if (parser === true || parser === "json") { + value = JSON.parse(value); + } else if (parser === "none") { + } else { + value = parser(value); + } + return value; } - const type = pathnameSplit[0]; - const data = pathnameSplit[1]; + parsers[type] = (pathname, opts) => { + const current: any = { type }; + for (const required in requiredFields) { + if (!(required in opts)) { + return undefined; + } + const parser = requiredFields[required]; + let value = opts[required]; + value = parse(parser, value); + if (value !== null && value !== undefined) { + current[required] = value; + } + } + for (const opt in optionalFields) { + if (!(opt in opts)) { + continue; + } + const parser = optionalFields[opt]; + let value = opts[opt]; + value = parse(parser, value); + if (value !== undefined) { + current[opt] = value; + } + } + if (customParser) { + const val = customParser(pathname, opts, current); + if (val === null) { + return undefined; + } else if (val === undefined) { + return current; + } else { + return val; + } + } + return current; + }; + } - if (type === "doc") { - const s = data.split("?"); - if (s.length < 1 || s.length > 2) { - return undefined; + function addStringifier(type: string, keys: string[], customStringifier?: (state: ParsedUrl, current: string) => string) { + stringifiers[type] = state => { + let path = Utils.prepend(`/${type}`); + if (customStringifier) { + path = customStringifier(state, path); } - const docId = s[0]; - const initializers = s.length === 2 ? JSON.parse(decodeURIComponent(s[1])) : {}; - return { - type: "doc", - docId, - initializers - }; + const queryObj = OmitKeys(state, keys).extract; + const query: any = {}; + Object.keys(queryObj).forEach(key => query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key])); + const queryString = qs.stringify(query); + return path + (queryString ? `?${queryString}` : ""); + }; + } + + addParser("doc", {}, { readonly: true, initializers: true, nro: true }, (pathname, opts, current) => { + if (pathname.length !== 2) return undefined; + + current.initializers = current.initializers || {}; + const docId = pathname[1]; + current.docId = docId; + }); + addStringifier("doc", ["initializers", "readonly", "nro"], (state, current) => { + return `${current}/${state.docId}`; + }); + + + export function parseUrl(location: Location | URL): ParsedUrl | undefined { + const pathname = location.pathname.substring(1); + const search = location.search; + const opts = qs.parse(search, { sort: false }); + let pathnameSplit = pathname.split("/"); + + const type = pathnameSplit[0]; + + if (type in parsers) { + return parsers[type](pathnameSplit, opts); } return undefined; } export function createUrl(params: ParsedUrl): string { - let baseUrl = DocServer.prepend(`/${params.type}`); - switch (params.type) { - case "doc": - const initializers = encodeURIComponent(JSON.stringify(params.initializers)); - const id = params.docId; - let url = baseUrl + `/${id}`; - if (Object.keys(params.initializers).length) { - url += `?${initializers}`; - } - return url; + if (params.type in stringifiers) { + return stringifiers[params.type](params); } return ""; } @@ -112,7 +185,10 @@ export namespace HistoryUtil { async function onDocUrl(url: DocUrl) { const field = await DocServer.GetRefField(url.docId); - await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id]))); + const init = url.initializers; + if (init) { + await Promise.all(Object.keys(init).map(id => initDoc(id, init[id]))); + } if (field instanceof Doc) { MainView.Instance.openWorkspace(field, true); } diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index ce95ba90e..75b0b52a7 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -8,7 +8,7 @@ import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; import Measure, { ContentRect } from "react-measure"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowUp, faTag, faPlus } from '@fortawesome/free-solid-svg-icons'; +import { faTag, faPlus, faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons'; import { Docs, DocumentOptions } from "../../documents/Documents"; import { observer } from "mobx-react"; import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from "./ImportMetadataEntry"; @@ -40,7 +40,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> constructor(props: FieldViewProps) { super(props); - library.add(faArrowUp, faTag, faPlus); + library.add(faTag, faPlus); let doc = this.props.Document; this.editingMetadata = this.editingMetadata || false; this.persistent = this.persistent || false; @@ -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.getDocumentFromType(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--); }); @@ -136,7 +136,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> }; let parent = this.props.ContainingCollectionView; if (parent) { - let importContainer = Docs.StackingDocument(docs, options); + let importContainer = Docs.Create.StackingDocument(docs, options); importContainer.singleColumn = false; Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer); !this.persistent && this.props.removeDocument && this.props.removeDocument(doc); @@ -237,12 +237,12 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> }} /> <div style={{ position: "absolute", - left: this.left + 12.6, - top: this.top + 11, + left: this.left + 8, + top: this.top + 10, opacity: uploading ? 0 : 1, transition: "0.4s opacity ease" }}> - <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} /> + <FontAwesomeIcon icon={faCloudUploadAlt} color="#FFFFFF" size={"2x"} /> </div> <img style={{ @@ -318,7 +318,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> opacity: uploading ? 0 : 1, transition: "0.4s opacity ease" }} - icon={isEditing ? faArrowUp : faTag} + icon={isEditing ? faCloudUploadAlt : faTag} color="#FFFFFF" size={"1x"} /> diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 944bc532f..a647f22c1 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -5,6 +5,7 @@ import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; import { Id } from "../../new_fields/FieldSymbols"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { Docs } from "../documents/Documents"; /* @@ -35,7 +36,9 @@ export class LinkManager { // the linkmanagerdoc stores a list of docs representing all linkdocs in 'allLinks' and a list of strings representing all group types in 'allGroupTypes' // lists of strings representing the metadata keys for each group type is stored under a key that is the same as the group type public get LinkManagerDoc(): Doc | undefined { - return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc)); + // return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc)); + + return Docs.Prototypes.MainLinkDocument(); } public getAllLinks(): Doc[] { @@ -232,11 +235,11 @@ export class LinkManager { // finds the opposite anchor of a given anchor in a link //TODO This should probably return undefined if there isn't an opposite anchor //TODO This should also await the return value of the anchor so we don't filter out promises - public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc { + public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined { if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) { - return Cast(linkDoc.anchor2, Doc, null)!; + return Cast(linkDoc.anchor2, Doc, null); } else { - return Cast(linkDoc.anchor1, Doc, null)!; + return Cast(linkDoc.anchor1, Doc, null); } } }
\ No newline at end of file diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a2842ca42..ce9e29b26 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -215,12 +215,13 @@ export const marks: { [index: string]: MarkSpec } = { link: { attrs: { href: {}, + location: { default: null }, title: { default: null } }, inclusive: false, parseDOM: [{ tag: "a[href]", getAttrs(dom: any) { - return { href: dom.getAttribute("href"), title: dom.getAttribute("title") }; + return { href: dom.getAttribute("href"), location: dom.getAttribute("location"), title: dom.getAttribute("title") }; } }], toDOM(node: any) { return ["a", node.attrs, 0]; } @@ -409,6 +410,20 @@ export const marks: { [index: string]: MarkSpec } = { }] }, + p18: { + parseDOM: [{ style: 'font-size: 18px;' }], + toDOM: () => ['span', { + style: 'font-size: 18px;' + }] + }, + + p20: { + parseDOM: [{ style: 'font-size: 20px;' }], + toDOM: () => ['span', { + style: 'font-size: 20px;' + }] + }, + p24: { parseDOM: [{ style: 'font-size: 24px;' }], toDOM: () => ['span', { diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 3156c4f43..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 @@ -33,6 +35,8 @@ export interface CompileError { errors: any[]; } +export type CompileResult = CompiledScript | CompileError; + export namespace Scripting { export function addGlobal(global: { name: string }): void; export function addGlobal(name: string, global: any): void; @@ -53,15 +57,36 @@ 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; -export type CompileResult = CompiledScript | CompileError; 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 ((options.typecheck !== false && errors) || !script) { @@ -161,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; @@ -168,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) { @@ -191,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(); @@ -202,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 338628960..ee5a83710 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -2,31 +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, returnDocs: true): Promise<DocSearchResult>; - export function Search(query: string, returnDocs: false): Promise<IdSearchResult>; - export async function Search(query: string, returnDocs: boolean) { - const result: IdSearchResult = JSON.parse(await rp.get(DocServer.prepend("/search"), { - qs: { query } + 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(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[]>; @@ -35,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/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index a7246d7c4..dca539f3b 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -1,5 +1,6 @@ import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr"; import { Field } from "../../new_fields/Doc"; +import { ClientUtils } from "./ClientUtils"; export namespace SerializationHelper { let serializing: number = 0; @@ -9,7 +10,7 @@ export namespace SerializationHelper { export function Serialize(obj: Field): any { if (obj === undefined || obj === null) { - return undefined; + return null; } if (typeof obj !== 'object') { @@ -38,7 +39,12 @@ export namespace SerializationHelper { serializing += 1; if (!obj.__type) { - throw Error("No property 'type' found in JSON."); + if (ClientUtils.RELEASE) { + console.warn("No property 'type' found in JSON."); + return undefined; + } else { + throw Error("No property 'type' found in JSON."); + } } if (!(obj.__type in serializationTypes)) { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 103157bbc..b3b7848e8 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -24,6 +24,7 @@ import { FormattedTextBoxProps, FormattedTextBox } from "../views/nodes/Formatte import { typeAlias } from "babel-types"; import React from "react"; import ReactDOM from "react-dom"; +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 { @@ -66,6 +67,7 @@ export class TooltipTextMenu { this.tooltip.className = "tooltipMenu"; this.dragElement(this.tooltip); + this.dragElement(this.tooltip); // this.createCollapse(); // if (this._collapseBtn) { // this.tooltip.appendChild(this._collapseBtn.render(this.view).dom); @@ -137,6 +139,8 @@ export class TooltipTextMenu { this.fontSizeToNum.set(schema.marks.p12, 12); this.fontSizeToNum.set(schema.marks.p14, 14); this.fontSizeToNum.set(schema.marks.p16, 16); + this.fontSizeToNum.set(schema.marks.p18, 18); + this.fontSizeToNum.set(schema.marks.p20, 20); this.fontSizeToNum.set(schema.marks.p24, 24); this.fontSizeToNum.set(schema.marks.p32, 32); this.fontSizeToNum.set(schema.marks.p48, 48); @@ -152,6 +156,7 @@ export class TooltipTextMenu { this.listTypes = Array.from(this.listTypeToIcon.keys()); //custom tools + // this.tooltip.appendChild(this.createLink().render(this.view).dom); this._brushdom = this.createBrush().render(this.view).dom; this.tooltip.appendChild(this._brushdom); @@ -283,8 +288,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)) { @@ -310,12 +315,21 @@ 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, { handlers: { dragComplete: action(() => { - let m = dragData.droppedDocuments; - this.makeLink(DocServer.prepend("/doc/" + m[0][Id])); + // let m = dragData.droppedDocuments; + let linkDoc = dragData.linkDocument; + 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 @@ -335,17 +349,17 @@ export class TooltipTextMenu { this.linkText.onkeydown = (e: KeyboardEvent) => { if (e.key === "Enter") { - this.makeLink(this.linkText!.textContent!); + // this.makeLink(this.linkText!.textContent!); e.stopPropagation(); e.preventDefault(); } }; - this.tooltip.appendChild(this.linkEditor); + // this.tooltip.appendChild(this.linkEditor); } - makeLink = (target: string) => { + makeLink = (target: string, location: string) => { let node = this.view.state.selection.$from.nodeAfter; - let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target }); + let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location }); this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); node = this.view.state.selection.$from.nodeAfter; @@ -586,7 +600,7 @@ export class TooltipTextMenu { let node = state.doc.nodeAt(from); node && node.marks.map(m => { m.type === markType && (curLink = m.attrs.href); - }) + }); //toggleMark(markType)(state, dispatch); //return true; } diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d index 2cbe1dd40..1f95af00c 100644 --- a/src/client/util/type_decls.d +++ b/src/client/util/type_decls.d @@ -204,3 +204,5 @@ declare const Docs: { TreeDocument(documents: Doc[], options?: DocumentOptions): Doc; StackingDocument(documents: Doc[], options?: DocumentOptions): Doc; }; + +declare function d(...args:any[]):any; |
