diff options
Diffstat (limited to 'src/client/util')
| -rw-r--r-- | src/client/util/CurrentUserUtils.ts | 116 | ||||
| -rw-r--r-- | src/client/util/DictationManager.ts | 2 | ||||
| -rw-r--r-- | src/client/util/DragManager.ts | 63 | ||||
| -rw-r--r-- | src/client/util/DropConverter.ts | 9 | ||||
| -rw-r--r-- | src/client/util/Import & Export/DirectoryImportBox.tsx | 36 | ||||
| -rw-r--r-- | src/client/util/InteractionUtils.tsx | 112 | ||||
| -rw-r--r-- | src/client/util/LinkManager.ts | 3 | ||||
| -rw-r--r-- | src/client/util/ScriptManager.ts | 104 | ||||
| -rw-r--r-- | src/client/util/Scripting.ts | 66 | ||||
| -rw-r--r-- | src/client/util/SettingsManager.tsx | 7 | ||||
| -rw-r--r-- | src/client/util/UndoManager.ts | 22 |
11 files changed, 387 insertions, 153 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 324b08603..7cc410a1c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -21,6 +21,7 @@ import { MainView } from "../views/MainView"; import { DocumentType } from "../documents/DocumentTypes"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; +import { LabelBox } from "../views/nodes/LabelBox"; export class CurrentUserUtils { private static curr_id: string; @@ -86,6 +87,52 @@ export class CurrentUserUtils { }); } + if (doc["template-button-link"] === undefined) { + const linkTemplate = Docs.Create.TextDocument(" ", { title: "header", _height: 100 }, "header"); // text needs to be a space to allow templateText to be created + Doc.GetProto(linkTemplate).layout = + "<div>" + + " <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`lightBlue`}' fieldKey={'header'}/>" + + " <FormattedTextBox {...props} position='absolute' top='{(this._headerHeight||75)*scale}px' height='calc({100/scale}% - {this._headerHeight||75}px)' fieldKey={'text'}/>" + + "</div>"; + linkTemplate.isTemplateDoc = makeTemplate(linkTemplate, true, "linkView"); + + const rtf2 = { + doc: { + type: "doc", content: [ + { + type: "paragraph", + content: [{ + type: "dashField", + attrs: { + fieldKey: "src", + hideKey: false + } + }] + }, + { type: "paragraph" }, + { + type: "paragraph", + content: [{ + type: "dashField", + attrs: { + fieldKey: "dst", + hideKey: false + } + }] + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + linkTemplate.header = new RichTextField(JSON.stringify(rtf2), ""); + + doc["template-button-link"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('makeDelegate(this.dragFactory)'), + dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, + removeDropProperties: new List<string>(["dropAction"]), title: "link view", icon: "window-maximize" + }); + } + if (doc["template-button-switch"] === undefined) { const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; @@ -168,17 +215,21 @@ export class CurrentUserUtils { }); } + const requiredTypes = [ + doc["template-button-slides"] as Doc, + doc["template-button-description"] as Doc, + doc["template-button-query"] as Doc, + doc["template-button-detail"] as Doc, + doc["template-button-link"] as Doc, + doc["template-button-switch"] as Doc]; if (doc["template-buttons"] === undefined) { - doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, - doc["template-button-query"] as Doc, doc["template-button-detail"] as Doc, doc["template-button-switch"] as Doc], { + doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, { title: "Advanced Item Prototypes", _xMargin: 0, _showTitle: "title", _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), })); } else { const curButnTypes = Cast(doc["template-buttons"], Doc, null); - const requiredTypes = [doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, - doc["template-button-query"] as Doc, doc["template-button-detail"] as Doc, doc["template-button-switch"] as Doc]; DocListCastAsync(curButnTypes.data).then(async curBtns => { await Promise.all(curBtns!); requiredTypes.map(btype => Doc.AddDocToList(curButnTypes, "data", btype)); @@ -262,9 +313,17 @@ export class CurrentUserUtils { iconView.isTemplateDoc = makeTemplate(iconView); doc["template-icon-view"] = new PrefetchProxy(iconView); } + if (doc["template-icon-view-pdf"] === undefined) { + const iconPdfView = Docs.Create.LabelDocument({ + title: "icon_" + DocumentType.PDF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") + }); + iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF); + doc["template-icon-view-pdf"] = new PrefetchProxy(iconPdfView); + } if (doc["template-icon-view-rtf"] === undefined) { const iconRtfView = Docs.Create.LabelDocument({ - title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", + title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") }); iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); @@ -284,11 +343,11 @@ export class CurrentUserUtils { } if (doc["template-icons"] === undefined) { doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc], { title: "icon templates", _height: 75 })); + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75 })); } else { const templateIconsDoc = Cast(doc["template-icons"], Doc, null); const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc]; DocListCastAsync(templateIconsDoc.data).then(async curIcons => { await Promise.all(curIcons!); requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype)); @@ -481,12 +540,17 @@ export class CurrentUserUtils { } if (doc["tabs-button-tools"] === undefined) { + const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { + _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true + })) as any as Doc; doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({ _width: 35, _height: 25, title: "Tools", _fontSize: 10, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { - _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true - })) as any as Doc, + sourcePanel: toolsStack, + onDragStart: ScriptField.MakeFunction('getAlias(this.dragFactory, true)'), + dragFactory: toolsStack, + removeDropProperties: new List<string>(["lockedPosition"]), + stayInCollection: true, targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"), })); @@ -510,8 +574,9 @@ export class CurrentUserUtils { } static setupCatalog(doc: Doc) { if (doc.myCatalog === undefined) { - doc.myCatalog = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "CATALOG", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, lockedPosition: true, + doc.myCatalog = new PrefetchProxy(Docs.Create.SchemaDocument([], [], { + title: "CATALOG", _height: 1000, _fitWidth: true, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, + childDropAction: "alias", targetDropAction: "same", treeViewExpandedView: "layout", stayInCollection: true, })); } return doc.myCatalog as Doc; @@ -520,7 +585,7 @@ export class CurrentUserUtils { // setup Recently Closed library item if (doc.myRecentlyClosed === undefined) { doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, + title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, stayInCollection: true, })); } // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready @@ -538,12 +603,18 @@ export class CurrentUserUtils { const recentlyClosed = CurrentUserUtils.setupRecentlyClosed(doc); if (doc["tabs-button-library"] === undefined) { + const libraryStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { + title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", + lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" + })) as any as Doc; doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({ - _width: 50, _height: 25, title: "Library", _fontSize: 10, + _width: 50, _height: 25, title: "Library", _fontSize: 10, targetDropAction: "same", letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { - title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true - })) as any as Doc, + sourcePanel: libraryStack, + onDragStart: ScriptField.MakeFunction('getAlias(this.dragFactory, true)'), + dragFactory: libraryStack, + removeDropProperties: new List<string>(["lockedPosition"]), + stayInCollection: true, targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;") })); @@ -686,6 +757,7 @@ export class CurrentUserUtils { } static async updateUserDocument(doc: Doc) { + doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; doc.activeInkPen = doc; doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); @@ -707,6 +779,7 @@ export class CurrentUserUtils { this.setupDefaultPresentation(doc); // presentation that's initially triggered await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument(); + doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); @@ -739,6 +812,9 @@ export class CurrentUserUtils { } } -Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); -Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }); -Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); });
\ No newline at end of file +Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }, + "initializes the Mobile inking document", "(userDoc: Doc)"); +Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }, + "initializes the Mobile upload document", "(userDoc: Doc)"); +Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }, + "creates a new workspace when called");
\ No newline at end of file diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index e46225b4a..d8a5657c3 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -144,7 +144,7 @@ export namespace DictationManager { recognizer.start(); return new Promise<string>((resolve, reject) => { - recognizer.onerror = (e: SpeechRecognitionError) => { + recognizer.onerror = (e: any) => { // e is SpeechRecognitionError but where is that defined? if (!(indefinite && e.error === "no-speech")) { recognizer.stop(); reject(e); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f1afaf734..26e7250f4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -13,7 +13,7 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { UndoManager } from "./UndoManager"; import { SnappingManager } from "./SnappingManager"; -export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move +export type dropActionType = "alias" | "copy" | "move" | "same" | undefined; // undefined = move export function SetupDrag( _reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc> | undefined, @@ -37,7 +37,7 @@ export function SetupDrag( dragData.treeViewId = treeViewId; dragData.dontHideOnDrop = dontHideOnDrop; DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y); - dragStarted && dragStarted(); + dragStarted?.(); } }; const onRowUp = (): void => { @@ -122,7 +122,7 @@ export namespace DragManager { export class DocumentDragData { constructor(dragDoc: Doc[]) { this.draggedDocuments = dragDoc; - this.droppedDocuments = dragDoc; + this.droppedDocuments = []; this.offset = [0, 0]; } draggedDocuments: Doc[]; @@ -134,7 +134,6 @@ export namespace DragManager { dropAction: dropActionType; removeDropProperties?: string[]; userDropAction: dropActionType; - embedDoc?: boolean; moveDocument?: MoveFunction; removeDocument?: RemoveFunction; isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged. this will suppress onDragStart scripts @@ -209,15 +208,19 @@ export namespace DragManager { }; const batch = UndoManager.StartBatch("dragging"); const finishDrag = (e: DragCompleteEvent) => { - e.docDragData && (e.docDragData.droppedDocuments = - dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) : - dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) : - dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeDelegate(d) : d) - ); - e.docDragData?.droppedDocuments.forEach((drop: Doc, i: number) => - (dragData?.removeDropProperties || []).concat(Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), [])).map(prop => drop[prop] = undefined) - ); - batch.end(); + const docDragData = e.docDragData; + if (docDragData && !docDragData.droppedDocuments.length) { + docDragData.dropAction = dragData.userDropAction || dragData.dropAction; + docDragData.droppedDocuments = + dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) : + docDragData.dropAction === "alias" ? Doc.MakeAlias(d) : + docDragData.dropAction === "copy" ? Doc.MakeDelegate(d) : d); + docDragData.dropAction !== "same" && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => + (dragData?.removeDropProperties || []).concat(Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), [])).map(prop => drop[prop] = undefined) + ); + batch.end(); + } + return e; }; dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded StartDrag(eles, dragData, downX, downY, options, finishDrag); @@ -231,6 +234,7 @@ export namespace DragManager { initialize?.(bd); Doc.GetProto(bd)["onClick-paramFieldKeys"] = new List<string>(params); e.docDragData && (e.docDragData.droppedDocuments = [bd]); + return e; }; StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag); } @@ -319,12 +323,12 @@ export namespace DragManager { dragDiv = document.createElement("div"); dragDiv.className = "dragManager-dragDiv"; dragDiv.style.pointerEvents = "none"; - dragLabel = document.createElement("div") as HTMLDivElement; + dragLabel = document.createElement("div"); dragLabel.className = "dragManager-dragLabel"; dragLabel.style.zIndex = "100001"; dragLabel.style.fontSize = "10"; dragLabel.style.position = "absolute"; - dragLabel.innerText = "press 'a' to embed on drop"; + // dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar dragDiv.appendChild(dragLabel); DragManager.Root().appendChild(dragDiv); } @@ -406,31 +410,28 @@ export namespace DragManager { const yFromTop = downY - elesCont.top; const xFromRight = elesCont.right - downX; const yFromBottom = elesCont.bottom - downY; - let alias = "alias"; const moveHandler = (e: PointerEvent) => { e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : undefined; } - if (e) - if (e.shiftKey && dragData.droppedDocuments.length === 1) { - !dragData.dropAction && (dragData.dropAction = alias); - if (dragData.dropAction === "move") { - dragData.removeDocument?.(dragData.draggedDocuments[0]); - } - AbortDrag(); - finishDrag?.(new DragCompleteEvent(true, dragData)); - DragManager.StartWindowDrag?.({ - pageX: e.pageX, - pageY: e.pageY, - preventDefault: emptyFunction, - button: 0 - }, dragData.droppedDocuments); + if (e?.shiftKey && dragData.draggedDocuments.length === 1) { + dragData.dropAction = dragData.userDropAction || "same"; + if (dragData.dropAction === "move") { + dragData.removeDocument?.(dragData.draggedDocuments[0]); } + AbortDrag(); + finishDrag?.(new DragCompleteEvent(true, dragData)); + DragManager.StartWindowDrag?.({ + pageX: e.pageX, + pageY: e.pageY, + preventDefault: emptyFunction, + button: 0 + }, dragData.droppedDocuments); + } const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom); - alias = "move"; const moveX = thisX - lastX; const moveY = thisY - lastY; lastX = thisX; diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 752c1cfc5..f9837298d 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -54,10 +54,12 @@ export function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string return any; } export function convertDropDataToButtons(data: DragManager.DocumentDragData) { - data && data.draggedDocuments.map((doc, i) => { + data?.draggedDocuments.map((doc, i) => { let dbox = doc; // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant - if (!doc.onDragStart && !doc.isButtonBar) { + if (doc.type === DocumentType.FONTICON) { + dbox = Doc.MakeAlias(doc); + } else if (!doc.onDragStart && !doc.isButtonBar) { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.type !== DocumentType.FONTICON) { !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc); @@ -76,4 +78,5 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data.droppedDocuments[i] = dbox; }); } -Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); });
\ No newline at end of file +Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }, + "converts the dropped data to buttons", "(dragData: any)");
\ No newline at end of file diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 25c556697..af6c57e68 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -1,33 +1,33 @@ -import "fs"; -import React = require("react"); -import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; -import { action, observable, runInAction, computed, reaction, IReactionDisposer } from "mobx"; -import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; -import Measure, { ContentRect } from "react-measure"; import { library } from '@fortawesome/fontawesome-svg-core'; +import { faCloudUploadAlt, faPlus, faTag } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faTag, faPlus, faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons'; -import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; +import { BatchedArray } from "array-batcher"; +import "fs"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from "./ImportMetadataEntry"; -import { Utils } from "../../../Utils"; -import { DocumentManager } from "../DocumentManager"; +import * as path from 'path'; +import Measure, { ContentRect } from "react-measure"; +import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; -import { Cast, BoolCast, NumCast } from "../../../fields/Types"; import { listSpec } from "../../../fields/Schema"; -import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; -import "./DirectoryImportBox.scss"; -import { Networking } from "../../Network"; -import { BatchedArray } from "array-batcher"; -import * as path from 'path'; +import { BoolCast, Cast, NumCast } from "../../../fields/Types"; import { AcceptibleMedia, Upload } from "../../../server/SharedMediaTypes"; +import { Utils } from "../../../Utils"; +import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; +import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; +import { Networking } from "../../Network"; +import { FieldView, FieldViewProps } from "../../views/nodes/FieldView"; +import { DocumentManager } from "../DocumentManager"; +import "./DirectoryImportBox.scss"; +import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from "./ImportMetadataEntry"; +import React = require("react"); const unsupported = ["text/html", "text/plain"]; @observer -export default class DirectoryImportBox extends React.Component<FieldViewProps> { +export class DirectoryImportBox extends React.Component<FieldViewProps> { private selector = React.createRef<HTMLInputElement>(); @observable private top = 0; @observable private left = 0; diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 68dcbebe3..9a9bb3d42 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -91,86 +91,66 @@ export namespace InteractionUtils { return myTouches; } - - export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string, bezier: string, fill: string, arrowStart: string, arrowEnd: string, dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean) { - var pts = ""; - if (shape) { - //if any of the shape are true - const shapePts = makePolygon(shape, points); - pts = shapePts.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X * scalex - left * scalex},${pt.Y * scaley - top * scaley} `, ""); + export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, + color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string, + dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean) { + let pts: { X: number; Y: number; }[] = []; + if (shape) { //if any of the shape are true + pts = makePolygon(shape, points); } else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) { //pointer is up (first and last points are the same) - const newPoints: number[][] = []; - const newPts: { X: number; Y: number; }[] = []; - //convert to [][] for fitcurve module - for (var i = 0; i < points.length - 2; i++) { - newPoints.push([points[i].X, points[i].Y]); - } + points.pop(); + const newPoints = points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]); + const bezierCurves = fitCurve(newPoints, parseInt(bezier)); - for (var i = 0; i < bezierCurves.length; i++) { + for (const curve of bezierCurves) { for (var t = 0; t < 1; t += 0.01) { - const point = beziercurve(t, bezierCurves[i]); - newPts.push({ X: point[0], Y: point[1] }); + const point = beziercurve(t, curve); + pts.push({ X: point[0], Y: point[1] }); } } - pts = newPts.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X * scalex - left * scalex},${pt.Y * scaley - top * scaley} `, ""); } else { - //in the middle of drawing - pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X * scalex - left * scalex},${pt.Y * scaley - top * scaley} `, ""); + pts = points; } + const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc + + `${(pt.X - left - width / 2) * scalex + width / 2}, + ${(pt.Y - top - width / 2) * scaley + width / 2} `, ""); const dashArray = String(Number(width) * Number(dash)); - return ( - <svg> - <defs> - <marker id="dot" orient="auto" overflow="visible"> - <circle r={0.5} fill="context-stroke" /> - </marker> - <marker id="arrowHead" orient="auto" overflow="visible" refX="3" refY="1" markerWidth="10" markerHeight="7"> - {/* <rect width={strokeWidth} height={strokeWidth} transform='rotate(45)' fill="dodgerblue" /> */} - <polygon points="3 0, 3 2, 0 1" fill="black" /> - </marker> - <marker id="arrowEnd" orient="auto" overflow="visible" refX="0" refY="1" markerWidth="10" markerHeight="7"> - {/* <rect width={strokeWidth} height={strokeWidth} transform='rotate(45)' fill="dodgerblue" /> */} - <polygon points="0 0, 3 1, 0 2" fill="black" /> - </marker> - - </defs> - {/* <polyline - points={pts} - style={{ - fill: fill, - pointerEvents: pevents as any, - stroke: drawHalo ? "grey" : "transparent", - strokeWidth: parseInt(width) * 4, - strokeLinejoin: "round", - strokeLinecap: "round", - strokeDasharray: dashArray - }} - markerStart={arrowStart} - markerEnd={arrowEnd} - /> */} + return (<svg> + <defs> + <marker id="dot" orient="auto" overflow="visible"> + <circle r={0.5} fill="context-stroke" /> + </marker> + <marker id="arrowHead" orient="auto" overflow="visible" refX="3" refY="1" markerWidth="10" markerHeight="7"> + {/* <rect width={strokeWidth} height={strokeWidth} transform='rotate(45)' fill="dodgerblue" /> */} + <polygon points="3 0, 3 2, 0 1" fill={fill} /> + </marker> + <marker id="arrowEnd" orient="auto" overflow="visible" refX="0" refY="1" markerWidth="10" markerHeight="7"> + {/* <rect width={strokeWidth} height={strokeWidth} transform='rotate(45)' fill="dodgerblue" /> */} + <polygon points="0 0, 3 1, 0 2" fill={fill} /> + </marker> - <polyline - points={pts} - style={{ - // filter: drawHalo ? "url(#dangerShine)" : undefined, - fill: fill, - pointerEvents: pevents as any, - stroke: color ?? "rgb(0, 0, 0)", - strokeWidth: parseInt(width), - strokeLinejoin: "round", - strokeLinecap: "round", - strokeDasharray: dashArray - }} - markerStart={arrowStart} - markerEnd={arrowEnd} - /> + </defs> - </svg> + <polyline + points={strpts} + style={{ + filter: drawHalo ? "url(#dangerShine)" : undefined, + fill: "none", + opacity: strokeWidth !== width ? 0.5 : undefined, + pointerEvents: pevents as any, + stroke: color ?? "rgb(0, 0, 0)", + strokeWidth: strokeWidth, + strokeLinejoin: "round", + strokeLinecap: "round" + }} + markerStart={arrowStart} + markerEnd={arrowEnd} + /> - ); + </svg>); } // export function makeArrow() { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 95528e25a..47b2541bd 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -205,4 +205,5 @@ export class LinkManager { } } -Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); });
\ No newline at end of file +Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, + "creates a link to inputted document", "(doc: any)");
\ No newline at end of file diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts new file mode 100644 index 000000000..785e63d9a --- /dev/null +++ b/src/client/util/ScriptManager.ts @@ -0,0 +1,104 @@ +import { Doc, DocListCast } from "../../fields/Doc"; +import { List } from "../../fields/List"; +import { Scripting } from "./Scripting"; +import { StrCast, Cast } from "../../fields/Types"; +import { listSpec } from "../../fields/Schema"; +import { Docs } from "../documents/Documents"; + +export class ScriptManager { + + static _initialized = false; + private static _instance: ScriptManager; + public static get Instance(): ScriptManager { + return this._instance || (this._instance = new this()); + } + private constructor() { + if (!ScriptManager._initialized) { + ScriptManager._initialized = true; + this.getAllScripts().forEach(scriptDoc => ScriptManager.addScriptToGlobals(scriptDoc)); + } + } + + public get ScriptManagerDoc(): Doc | undefined { + return Docs.Prototypes.MainScriptDocument(); + } + public getAllScripts(): Doc[] { + const sdoc = ScriptManager.Instance.ScriptManagerDoc; + if (sdoc) { + const docs = DocListCast(sdoc.data); + return docs; + } + return []; + } + + public addScript(scriptDoc: Doc): boolean { + + console.log("in add script method"); + + const scriptList = this.getAllScripts(); + scriptList.push(scriptDoc); + if (ScriptManager.Instance.ScriptManagerDoc) { + ScriptManager.Instance.ScriptManagerDoc.data = new List<Doc>(scriptList); + ScriptManager.addScriptToGlobals(scriptDoc); + console.log("script added"); + return true; + } + return false; + } + + public deleteScript(scriptDoc: Doc): boolean { + + console.log("in delete script method"); + + if (scriptDoc.name) { + Scripting.removeGlobal(StrCast(scriptDoc.name)); + } + const scriptList = this.getAllScripts(); + const index = scriptList.indexOf(scriptDoc); + if (index > -1) { + scriptList.splice(index, 1); + if (ScriptManager.Instance.ScriptManagerDoc) { + ScriptManager.Instance.ScriptManagerDoc.data = new List<Doc>(scriptList); + return true; + } + } + return false; + } + + public static addScriptToGlobals(scriptDoc: Doc): void { + + Scripting.removeGlobal(StrCast(scriptDoc.name)); + + const params = Cast(scriptDoc["data-params"], listSpec("string"), []); + console.log(params); + const paramNames = params.reduce((o: string, p: string) => { + if (params.indexOf(p) === params.length - 1) { + o = o + p.split(":")[0].trim(); + } else { + o = o + p.split(":")[0].trim() + ","; + } + return o; + }, "" as string); + + const f = new Function(paramNames, StrCast(scriptDoc.script)); + + console.log(scriptDoc.script); + + Object.defineProperty(f, 'name', { value: StrCast(scriptDoc.name), writable: false }); + + let parameters = "("; + params.forEach((element: string, i: number) => { + if (i === params.length - 1) { + parameters = parameters + element + ")"; + } else { + parameters = parameters + element + ", "; + } + }); + + if (parameters === "(") { + Scripting.addGlobal(f, StrCast(scriptDoc.description)); + } else { + Scripting.addGlobal(f, StrCast(scriptDoc.description), parameters); + } + } +}
\ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index ab577315c..e6cf50de3 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -10,6 +10,8 @@ export { ts }; // @ts-ignore import * as typescriptlib from '!!raw-loader!./type_decls.d'; import { Doc, Field } from '../../fields/Doc'; +import { Cast } from "../../fields/Types"; +import { listSpec } from "../../fields/Schema"; export interface ScriptSucccess { success: true; @@ -49,19 +51,34 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is export namespace Scripting { export function addGlobal(global: { name: string }): void; export function addGlobal(name: string, global: any): void; - export function addGlobal(nameOrGlobal: any, global?: any) { - let n: string; + + export function addGlobal(global: { name: string }, decription?: string, params?: string): void; + + export function addGlobal(first: any, second?: any, third?: string) { + let n: any; let obj: any; - if (global !== undefined && typeof nameOrGlobal === "string") { - n = nameOrGlobal; - obj = global; - } else if (nameOrGlobal && typeof nameOrGlobal.name === "string") { - n = nameOrGlobal.name; - obj = nameOrGlobal; + + if (second !== undefined) { + if (typeof first === "string") { + n = first; + obj = second; + } else { + obj = first; + n = first.name; + _scriptingDescriptions[n] = second; + if (third !== undefined) { + _scriptingParams[n] = third; + } + } + } else if (first && typeof first.name === "string") { + n = first.name; + obj = first; } else { throw new Error("Must either register an object with a name, or give a name and an object"); } - if (_scriptingGlobals.hasOwnProperty(n)) { + if (n === undefined || n === "undefined") { + return false; + } else if (_scriptingGlobals.hasOwnProperty(n)) { throw new Error(`Global with name ${n} is already registered, choose another name`); } _scriptingGlobals[n] = obj; @@ -75,6 +92,20 @@ export namespace Scripting { scriptingGlobals = globals; } + export function removeGlobal(name: string) { + if (getGlobals().includes(name)) { + delete _scriptingGlobals[name]; + if (_scriptingDescriptions[name]){ + delete _scriptingDescriptions[name]; + } + if (_scriptingParams[name]){ + delete _scriptingParams[name]; + } + return true; + } + return false; + } + export function resetScriptingGlobals() { scriptingGlobals = _scriptingGlobals; } @@ -85,7 +116,19 @@ export namespace Scripting { } export function getGlobals() { - return Object.keys(scriptingGlobals); + return Object.keys(_scriptingGlobals); + } + + export function getGlobalObj() { + return _scriptingGlobals; + } + + export function getDescriptions(){ + return _scriptingDescriptions; + } + + export function getParameters(){ + return _scriptingParams; } } @@ -95,6 +138,8 @@ export function scriptingGlobal(constructor: { new(...args: any[]): any }) { const _scriptingGlobals: { [name: string]: any } = {}; let scriptingGlobals: { [name: string]: any } = _scriptingGlobals; +const _scriptingDescriptions: { [name: string]: any } = {}; +const _scriptingParams: { [name: string]: any } = {}; function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error); @@ -133,6 +178,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an } return { success: true, result }; } catch (error) { + if (batch) { batch.end(); } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 0e15197c4..9888cce48 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -10,6 +10,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Networking } from "../Network"; import { CurrentUserUtils } from "./CurrentUserUtils"; import { Utils } from "../../Utils"; +import { Doc } from "../../fields/Doc"; library.add(fa.faWindowClose); @@ -78,6 +79,10 @@ export default class SettingsManager extends React.Component<{}> { this.errorText = ""; this.successText = ""; } + @action + noviceToggle = (event: any) => { + Doc.UserDoc().noviceMode = !Doc.UserDoc().noviceMode; + } private get settingsInterface() { return ( @@ -91,7 +96,7 @@ export default class SettingsManager extends React.Component<{}> { <div className="settings-body"> <div className="settings-type"> <button onClick={this.onClick} value="password">reset password</button> - <button onClick={this.onClick} value="data">reset data</button> + <button onClick={this.noviceToggle} value="data">{`toggle ${Doc.UserDoc().noviceMode ? "developer" : "novice"} mode`}</button> <button onClick={() => window.location.assign(Utils.prepend("/logout"))}> {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"} </button> diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 314b52bf3..c7b7bb215 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -78,10 +78,12 @@ export namespace UndoManager { let currentBatch: UndoBatch | undefined; let batchCounter = 0; let undoing = false; + let tempEvents: UndoEvent[] | undefined = undefined; export function AddEvent(event: UndoEvent): void { if (currentBatch && batchCounter && !undoing) { currentBatch.push(event); + tempEvents?.push(event); } } @@ -135,7 +137,7 @@ export namespace UndoManager { const EndBatch = action((cancel: boolean = false) => { batchCounter--; - if (batchCounter === 0 && currentBatch && currentBatch.length) { + if (batchCounter === 0 && currentBatch?.length) { if (!cancel) { undoStack.push(currentBatch); } @@ -144,6 +146,13 @@ export namespace UndoManager { } }); + export function ClearTempBatch() { + tempEvents = undefined; + } + export function RunInTempBatch<T>(fn: () => T) { + tempEvents = []; + return runInAction(fn); + } //TODO Make this return the return value export function RunInBatch<T>(fn: () => T, batchName: string) { const batch = StartBatch(batchName); @@ -153,7 +162,16 @@ export namespace UndoManager { batch.end(); } } - + export const UndoTempBatch = action(() => { + if (tempEvents) { + undoing = true; + for (let i = tempEvents.length - 1; i >= 0; i--) { + tempEvents[i].undo(); + } + undoing = false; + } + tempEvents = undefined; + }); export const Undo = action(() => { if (undoStack.length === 0) { return; |
