aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts740
-rw-r--r--src/client/util/DictationManager.ts12
-rw-r--r--src/client/util/DocumentManager.ts46
-rw-r--r--src/client/util/DragManager.ts96
-rw-r--r--src/client/util/DropConverter.ts12
-rw-r--r--src/client/util/History.ts2
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx12
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts8
-rw-r--r--src/client/util/Import & Export/ImportMetadataEntry.tsx4
-rw-r--r--src/client/util/InteractionUtils.tsx8
-rw-r--r--src/client/util/LinkManager.ts8
-rw-r--r--src/client/util/Scripting.ts4
-rw-r--r--src/client/util/SearchUtil.ts4
-rw-r--r--src/client/util/SelectionManager.ts7
-rw-r--r--src/client/util/SerializationHelper.ts2
-rw-r--r--src/client/util/SettingsManager.tsx5
-rw-r--r--src/client/util/SharingManager.tsx6
-rw-r--r--src/client/util/SnappingManager.ts29
18 files changed, 897 insertions, 108 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
new file mode 100644
index 000000000..1f25ed790
--- /dev/null
+++ b/src/client/util/CurrentUserUtils.ts
@@ -0,0 +1,740 @@
+import { computed, observable, reaction } from "mobx";
+import * as rp from 'request-promise';
+import { Utils } from "../../Utils";
+import { DocServer } from "../DocServer";
+import { Docs, DocumentOptions } from "../documents/Documents";
+import { UndoManager } from "./UndoManager";
+import { Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { listSpec } from "../../fields/Schema";
+import { ScriptField, ComputedField } from "../../fields/ScriptField";
+import { Cast, PromiseValue, StrCast, NumCast } from "../../fields/Types";
+import { nullAudio } from "../../fields/URLField";
+import { DragManager } from "./DragManager";
+import { InkingControl } from "../views/InkingControl";
+import { Scripting } from "./Scripting";
+import { CollectionViewType } from "../views/collections/CollectionView";
+import { makeTemplate } from "./DropConverter";
+import { RichTextField } from "../../fields/RichTextField";
+import { PrefetchProxy } from "../../fields/Proxy";
+import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
+import { MainView } from "../views/MainView";
+import { DocumentType } from "../documents/DocumentTypes";
+import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
+import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
+
+export class CurrentUserUtils {
+ private static curr_id: string;
+ //TODO tfs: these should be temporary...
+ private static mainDocId: string | undefined;
+
+ public static get id() { return this.curr_id; }
+ public static get MainDocId() { return this.mainDocId; }
+ public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
+ @computed public static get UserDocument() { return Doc.UserDoc(); }
+ @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).inkPen as Doc; }
+
+ @observable public static GuestTarget: Doc | undefined;
+ @observable public static GuestWorkspace: Doc | undefined;
+ @observable public static GuestMobile: Doc | undefined;
+
+ // sets up the default User Templates - slideView, queryView, descriptionView
+ static setupUserTemplateButtons(doc: Doc) {
+ if (doc["template-button-query"] === undefined) {
+ const queryTemplate = Docs.Create.MulticolumnDocument(
+ [
+ Docs.Create.QueryDocument({ title: "query", _height: 200 }),
+ Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true })
+ ],
+ { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
+ );
+ queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
+ doc["template-button-query"] = CurrentUserUtils.ficon({
+ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
+ removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle"
+ });
+ }
+
+ if (doc["template-button-slides"] === undefined) {
+ const slideTemplate = Docs.Create.MultirowDocument(
+ [
+ Docs.Create.MulticolumnDocument([], { title: "data", _height: 200 }),
+ Docs.Create.TextDocument("", { title: "text", _height: 100 })
+ ],
+ { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
+ );
+ slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
+ doc["template-button-slides"] = CurrentUserUtils.ficon({
+ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ dragFactory: new PrefetchProxy(slideTemplate) as any as Doc,
+ removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card"
+ });
+ }
+
+ if (doc["template-button-description"] === undefined) {
+ const descriptionTemplate = Docs.Create.TextDocument(" ", { title: "header", _height: 100 }, "header"); // text needs to be a space to allow templateText to be created
+ Doc.GetProto(descriptionTemplate).layout =
+ "<div>" +
+ " <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`orange`}' fieldKey={'header'}/>" +
+ " <FormattedTextBox {...props} position='absolute' top='{(this._headerHeight||75)*scale}px' height='calc({100/scale}% - {this._headerHeight||75}px)' fieldKey={'text'}/>" +
+ "</div>";
+ descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView");
+
+ doc["template-button-description"] = CurrentUserUtils.ficon({
+ onDragStart: ScriptField.MakeFunction('makeDelegate(this.dragFactory)'),
+ dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc,
+ removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize"
+ });
+ }
+
+ if (doc["template-button-switch"] === undefined) {
+ const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
+
+ const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _LODdisable: true, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40 });
+ const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1 });
+ const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, _LODdisable: true });
+ const labelTemplate = {
+ doc: {
+ type: "doc", content: [{
+ type: "paragraph",
+ content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }]
+ }]
+ },
+ selection: { type: "text", anchor: 1, head: 1 },
+ storedMarks: []
+ };
+ Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS");
+ Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'");
+ // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'");
+ // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true");
+ Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]");
+ // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false");
+ const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, });
+ box.isTemplateDoc = makeTemplate(box, true, "switch");
+
+ doc["template-button-switch"] = CurrentUserUtils.ficon({
+ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ dragFactory: new PrefetchProxy(box) as any as Doc,
+ removeDropProperties: new List<string>(["dropAction"]), title: "data switch", icon: "toggle-on"
+ });
+ }
+
+ if (doc["template-button-detail"] === undefined) {
+ const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create;
+
+ const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)");
+ const carousel = CarouselDocument([], {
+ title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10,
+ onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F"
+ });
+
+ const details = TextDocument("", { title: "details", _height: 350, _autoHeight: true });
+ const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 100, _autoHeight: true });
+ const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 350, _autoHeight: true });
+
+ const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"];
+ const detailedTemplate = {
+ doc: {
+ type: "doc", content: buxtonFieldKeys.map(fieldKey => ({
+ type: "paragraph",
+ content: [{ type: "dashField", attrs: { fieldKey } }]
+ }))
+ },
+ selection: { type: "text", anchor: 1, head: 1 },
+ storedMarks: []
+ };
+ details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" "));
+
+ const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 };
+ const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: 12 };
+ const descriptionWrapperOpts = { title: "descriptions", _height: 300, columnWidth: -1, treeViewHideTitle: true, _pivotField: "title" };
+
+ const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
+ descriptionWrapper.sectionHeaders = new List<SchemaHeaderField>([
+ new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false),
+ new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true),
+ new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true),
+ ]);
+ const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts });
+ detailView.isTemplateDoc = makeTemplate(detailView);
+
+ details.title = "Details";
+ short.title = "A Short Description";
+ long.title = "Long Description";
+
+ doc["template-button-detail"] = CurrentUserUtils.ficon({
+ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ dragFactory: new PrefetchProxy(detailView) as any as Doc,
+ removeDropProperties: new List<string>(["dropAction"]), title: "detail view", icon: "window-maximize"
+ });
+ }
+
+ 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], {
+ 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));
+ });
+ }
+ return doc["template-buttons"] as Doc;
+ }
+
+ // setup the different note type skins
+ static setupNoteTemplates(doc: Doc) {
+ if (doc["template-note-Note"] === undefined) {
+ const noteView = Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" });
+ noteView.isTemplateDoc = makeTemplate(noteView, true, "Note");
+ doc["template-note-Note"] = new PrefetchProxy(noteView);
+ }
+ if (doc["template-note-Idea"] === undefined) {
+ const noteView = Docs.Create.TextDocument("", { title: "text", style: "Idea", backgroundColor: "pink" });
+ noteView.isTemplateDoc = makeTemplate(noteView, true, "Idea");
+ doc["template-note-Idea"] = new PrefetchProxy(noteView);
+ }
+ if (doc["template-note-Topic"] === undefined) {
+ const noteView = Docs.Create.TextDocument("", { title: "text", style: "Topic", backgroundColor: "lightBlue" });
+ noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic");
+ doc["template-note-Topic"] = new PrefetchProxy(noteView);
+ }
+ if (doc["template-note-Todo"] === undefined) {
+ const noteView = Docs.Create.TextDocument("", {
+ title: "text", style: "Todo", backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption",
+ layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus")
+ });
+ noteView.isTemplateDoc = makeTemplate(noteView, true, "Todo");
+ doc["template-note-Todo"] = new PrefetchProxy(noteView);
+ }
+ const taskStatusValues = [
+ { title: "todo", _backgroundColor: "blue", color: "white" },
+ { title: "in progress", _backgroundColor: "yellow", color: "black" },
+ { title: "completed", _backgroundColor: "green", color: "white" }
+ ];
+ if (doc.fieldTypes === undefined) {
+ doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" });
+ Doc.addFieldEnumerations(Doc.GetProto(doc["template-note-Todo"] as any as Doc), "taskStatus", taskStatusValues);
+ }
+
+ if (doc["template-notes"] === undefined) {
+ doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-note-Note"] as any as Doc,
+ doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc, doc["template-note-Todo"] as any as Doc],
+ { title: "Note Layouts", _height: 75 }));
+ } else {
+ const curNoteTypes = Cast(doc["template-notes"], Doc, null);
+ const requiredTypes = [doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc,
+ doc["template-note-Topic"] as any as Doc, doc["template-note-Todo"] as any as Doc];
+ DocListCastAsync(curNoteTypes.data).then(async curNotes => {
+ await Promise.all(curNotes!);
+ requiredTypes.map(ntype => Doc.AddDocToList(curNoteTypes, "data", ntype));
+ });
+ }
+
+ return doc["template-notes"] as Doc;
+ }
+
+ // creates Note templates, and initial "user" templates
+ static setupDocTemplates(doc: Doc) {
+ const noteTemplates = CurrentUserUtils.setupNoteTemplates(doc);
+ const userTemplateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
+ const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc);
+ if (doc.templateDocs === undefined) {
+ doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], {
+ title: "template layouts", _xPadding: 0,
+ dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
+ }));
+ }
+ }
+
+ // setup templates for different document types when they are iconified from Document Decorations
+ static setupDefaultIconTemplates(doc: Doc) {
+ if (doc["template-icon-view"] === undefined) {
+ const iconView = Docs.Create.TextDocument("", {
+ title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
+ });
+ Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
+ iconView.isTemplateDoc = makeTemplate(iconView);
+ doc["template-icon-view"] = new PrefetchProxy(iconView);
+ }
+ if (doc["template-icon-view-rtf"] === undefined) {
+ const iconRtfView = Docs.Create.LabelDocument({
+ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset",
+ _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
+ });
+ iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF);
+ doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView);
+ }
+ if (doc["template-icon-view-img"] === undefined) {
+ const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", {
+ title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)")
+ });
+ iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
+ doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
+ }
+ if (doc["template-icon-view-col"] === undefined) {
+ const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") });
+ iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
+ doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
+ }
+ 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 }));
+ } 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];
+ DocListCastAsync(templateIconsDoc.data).then(async curIcons => {
+ await Promise.all(curIcons!);
+ requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype));
+ });
+ }
+ return doc["template-icons"] as Doc;
+ }
+
+ static creatorBtnDescriptors(doc: Doc): {
+ title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean,
+ click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc
+ }[] {
+ if (doc.emptyPresentation === undefined) {
+ doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
+ { title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias", _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" });
+ }
+ if (doc.emptyCollection === undefined) {
+ doc.emptyCollection = Docs.Create.FreeformDocument([],
+ { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
+ }
+ if (doc.emptyDocHolder === undefined) {
+ doc.emptyDocHolder = Docs.Create.DocumentDocument(
+ ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]") as any,
+ { _width: 250, _height: 250, title: "container" });
+ }
+ if (doc.emptyWebpage === undefined) {
+ doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600, UseCors: true });
+ }
+ return [
+ { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
+ { title: "Drag a web page", label: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc },
+ { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' },
+ { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' },
+ { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
+ { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
+ { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding:10, _yPadding: 10, title: "Button" })' },
+ { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc },
+ { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' },
+ { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
+ { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
+ { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
+ { title: "Drag an instance of the device collection", label: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' },
+ // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc },
+ // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc },
+ { title: "Drag a document previewer", label: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.emptyDocHolder as Doc },
+ { title: "Toggle a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
+ { title: "Connect a Google Account", label: "Google Account", icon: "external-link-alt", click: 'GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' },
+ ];
+
+ }
+
+ // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
+ static async setupCreatorButtons(doc: Doc) {
+ let alreadyCreatedButtons: string[] = [];
+ const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null);
+ if (dragCreatorSet) {
+ const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc));
+ if (dragCreators) {
+ const dragDocs = await Promise.all(dragCreators);
+ alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
+ }
+ }
+ const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title));
+ const creatorBtns = buttons.map(({ title, label, icon, ignoreClick, drag, click, ischecked, activePen, backgroundColor, dragFactory }) => Docs.Create.FontIconDocument({
+ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
+ icon,
+ title,
+ label,
+ ignoreClick,
+ dropAction: "copy",
+ onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined,
+ onClick: click ? ScriptField.MakeScript(click) : undefined,
+ ischecked: ischecked ? ComputedField.MakeFunction(ischecked) : undefined,
+ activePen,
+ backgroundColor,
+ removeDropProperties: new List<string>(["dropAction"]),
+ dragFactory,
+ }));
+
+ if (dragCreatorSet === undefined) {
+ doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
+ title: "Basic Item Creators", _showTitle: "title", _xMargin: 0,
+ _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+ dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+ }));
+ } else {
+ creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb));
+ }
+ return doc.myItemCreators as Doc;
+ }
+
+ static setupMobileButtons(doc: Doc, buttons?: string[]) {
+ const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
+ { title: "record", icon: "microphone", ignoreClick: true, click: "FILL" },
+ { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc },
+ { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc },
+ // { title: "draw", icon: "pen-nib", click: 'switchMobileView(setupMobileInkingDoc, renderMobileInking, onSwitchMobileInking);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "red", activePen: doc },
+ { title: "upload", icon: "upload", click: 'switchMobileView(setupMobileUploadDoc, renderMobileUpload, onSwitchMobileUpload);', backgroundColor: "orange" },
+ // { title: "upload", icon: "upload", click: 'uploadImageMobile();', backgroundColor: "cyan" },
+ ];
+ return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({
+ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick,
+ onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
+ ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen,
+ backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory,
+ }));
+ }
+
+ static setupThumbButtons(doc: Doc) {
+ const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, clipboard?: Doc, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
+ { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc },
+ ];
+ return docProtoData.map(data => Docs.Create.FontIconDocument({
+ _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon,
+ dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick,
+ onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
+ clipboard: data.clipboard,
+ onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
+ ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true,
+ backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory,
+ }));
+ }
+
+ static setupThumbDoc(userDoc: Doc) {
+ if (!userDoc.thumbDoc) {
+ const thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), {
+ _width: 100, _height: 50, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons",
+ _autoHeight: true, _yMargin: 5, linearViewIsExpanded: true, backgroundColor: "white"
+ });
+ thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
+ _width: 300, _height: 25, _autoHeight: true, _chromeStatus: "disabled", linearViewIsExpanded: true, flexDirection: "column"
+ });
+ userDoc.thumbDoc = thumbDoc;
+ }
+ return Cast(userDoc.thumbDoc, Doc);
+ }
+
+ static setupMobileDoc(userDoc: Doc) {
+ return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), {
+ columnWidth: 100, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", _autoHeight: true, _yMargin: 5
+ });
+ }
+
+ static setupMobileInkingDoc(userDoc: Doc) {
+ return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white" });
+ }
+
+ static setupMobileUploadDoc(userDoc: Doc) {
+ // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" })
+ const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", {
+ title: "Upload Images From the Web", _chromeStatus: "enabled", lockedPosition: true
+ });
+ const uploadDoc = Docs.Create.StackingDocument([], {
+ title: "Mobile Upload Collection", backgroundColor: "white", lockedPosition: true
+ });
+ return Docs.Create.StackingDocument([webDoc, uploadDoc], {
+ _width: screen.width, lockedPosition: true, _chromeStatus: "disabled", title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray"
+ });
+ }
+
+ // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker.
+ // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
+ static async setupToolsBtnPanel(doc: Doc, sidebarContainer: Doc) {
+ // setup a masonry view of all he creators
+ const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
+ const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc);
+
+ if (doc.myCreators === undefined) {
+ doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
+ title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0,
+ _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled",
+ }));
+ }
+ // setup a color picker
+ if (doc.myColorPicker === undefined) {
+ const color = Docs.Create.ColorDocument({
+ title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
+ });
+ doc.myColorPicker = new PrefetchProxy(color);
+ }
+
+ if (doc["tabs-button-tools"] === undefined) {
+ 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,
+ targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+ onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
+ }));
+ }
+ (doc["tabs-button-tools"] as Doc).sourcePanel; // prefetch sourcePanel
+ return doc["tabs-button-tools"] as Doc;
+ }
+
+ static setupWorkspaces(doc: Doc) {
+ // setup workspaces library item
+ if (doc.myWorkspaces === undefined) {
+ doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true,
+ }));
+ }
+ const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
+ (doc.myWorkspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
+ (doc.myWorkspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
+
+ return doc.myWorkspaces as Doc;
+ }
+ 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,
+ }));
+ }
+ return doc.myCatalog as Doc;
+ }
+ static setupRecentlyClosed(doc: Doc) {
+ // 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,
+ }));
+ }
+ // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
+ PromiseValue(Cast(doc.myRecentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
+ const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
+ (doc.myRecentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+ (doc.myRecentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+
+ return doc.myRecentlyClosed as Doc;
+ }
+ // setup the Library button which will display the library panel. This panel includes a collection of workspaces, documents, and recently closed views
+ static setupLibraryPanel(doc: Doc, sidebarContainer: Doc) {
+ const workspaces = CurrentUserUtils.setupWorkspaces(doc);
+ const documents = CurrentUserUtils.setupCatalog(doc);
+ const recentlyClosed = CurrentUserUtils.setupRecentlyClosed(doc);
+
+ if (doc["tabs-button-library"] === undefined) {
+ doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+ _width: 50, _height: 25, title: "Library", _fontSize: 10,
+ 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,
+ targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+ onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
+ }));
+ }
+ return doc["tabs-button-library"] as Doc;
+ }
+
+ // setup the Search button which will display the search panel.
+ static setupSearchBtnPanel(doc: Doc, sidebarContainer: Doc) {
+ if (doc["tabs-button-search"] === undefined) {
+ doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({
+ _width: 50, _height: 25, title: "Search", _fontSize: 10,
+ letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
+ sourcePanel: new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc,
+ searchFileTypes: new List<string>([DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.VID, DocumentType.WEB, DocumentType.SCRIPTING]),
+ targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
+ lockedPosition: true,
+ onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel")
+ }));
+ }
+ return doc["tabs-button-search"] as Doc;
+ }
+
+ static setupSidebarContainer(doc: Doc) {
+ if (doc["tabs-panelContainer"] === undefined) {
+ const sidebarContainer = new Doc();
+ sidebarContainer._chromeStatus = "disabled";
+ sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()");
+ doc["tabs-panelContainer"] = new PrefetchProxy(sidebarContainer);
+ }
+ return doc["tabs-panelContainer"] as Doc;
+ }
+
+ // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
+ static async setupSidebarButtons(doc: Doc) {
+ const sidebarContainer = CurrentUserUtils.setupSidebarContainer(doc);
+ const toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer);
+ const libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer);
+ const searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer);
+
+ // Finally, setup the list of buttons to display in the sidebar
+ if (doc["tabs-buttons"] === undefined) {
+ doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], {
+ _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode",
+ title: "sidebar btn row stack", backgroundColor: "dimGray",
+ }));
+ (toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn });
+ }
+ }
+
+ static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
+ ...opts,
+ _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true,
+ dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
+ backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true
+ })) as any as Doc
+
+ static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
+ ...opts,
+ dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100
+ })) as any as Doc
+
+ /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
+ static setupDockedButtons(doc: Doc) {
+ if (doc["dockedBtn-pen"] === undefined) {
+ doc["dockedBtn-pen"] = CurrentUserUtils.ficon({
+ onClick: ScriptField.MakeScript("activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this, this.inkWidth, this.backgroundColor)"),
+ author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.inkPen, this)`), activePen: doc
+ });
+ }
+ if (doc["dockedBtn-undo"] === undefined) {
+ doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" });
+ }
+ if (doc["dockedBtn-redo"] === undefined) {
+ doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" });
+ }
+ if (doc.dockedBtns === undefined) {
+ doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc, doc["dockedBtn-pen"] as Doc]);
+ }
+ }
+ // sets up the default set of documents to be shown in the Overlay layer
+ static setupOverlays(doc: Doc) {
+ if (doc.myOverlayDocuments === undefined) {
+ doc.myOverlayDocuments = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6" }));
+ }
+ }
+
+ // the initial presentation Doc to use
+ static setupDefaultPresentation(doc: Doc) {
+ if (doc.activePresentation === undefined) {
+ doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
+ title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
+ _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0"
+ });
+ }
+ }
+
+ static setupRightSidebar(doc: Doc) {
+ if (doc.rightSidebarCollection === undefined) {
+ doc.rightSidebarCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Right Sidebar" }));
+ }
+ }
+
+ static setupClickEditorTemplates(doc: Doc) {
+ if (doc["clickFuncs-child"] === undefined) {
+ const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
+ "docCast(thisContainer.target).then((target) => {" +
+ " target && docCast(this.source).then((source) => { " +
+ " target.proto.data = new List([source || this]); " +
+ " }); " +
+ "})",
+ { target: Doc.name }), { title: "Click to open in target", _width: 300, _height: 200, targetScriptKey: "onChildClick" });
+
+ const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
+ "openOnRight(self.doubleClickView)",
+ { target: Doc.name }), { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick" });
+
+ doc["clickFuncs-child"] = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates" });
+ }
+ // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved.
+ PromiseValue(Cast(doc["clickFuncs-child"], Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
+
+ if (doc.clickFuncs === undefined) {
+ const onClick = Docs.Create.ScriptingDocument(undefined, {
+ title: "onClick", "onClick-rawScript": "console.log('click')",
+ isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200
+ }, "onClick");
+ const onDoubleClick = Docs.Create.ScriptingDocument(undefined, {
+ title: "onDoubleClick", "onDoubleClick-rawScript": "console.log('double click')",
+ isTemplateDoc: true, isTemplateForField: "onDoubleClick", _width: 300, _height: 200
+ }, "onDoubleClick");
+ const onCheckedClick = Docs.Create.ScriptingDocument(undefined, {
+ title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List<string>(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200
+ }, "onCheckedClick");
+ doc.clickFuncs = Docs.Create.TreeDocument([onClick, onDoubleClick, onCheckedClick], { title: "onClick funcs" });
+ }
+ PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
+
+ return doc.clickFuncs as Doc;
+ }
+
+ static async updateUserDocument(doc: Doc) {
+ new InkingControl();
+ doc.title = Doc.CurrentUserEmail;
+ doc.activePen = doc;
+ doc.inkColor = StrCast(doc.backgroundColor, "rgb(0, 0, 0)");
+ doc.fontSize = NumCast(doc.fontSize, 12);
+ doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); //
+ doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); //
+ Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]);
+ this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon
+ this.setupDocTemplates(doc); // sets up the template menu of templates
+ this.setupRightSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing
+ this.setupOverlays(doc); // documents in overlay layer
+ this.setupDockedButtons(doc); // the bottom bar of font icons
+ 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();
+
+ // 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 });
+ doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
+
+ return doc;
+ }
+ public static async loadCurrentUser() {
+ return rp.get(Utils.prepend("/getCurrentUser")).then(response => {
+ if (response) {
+ const result: { id: string, email: string } = JSON.parse(response);
+ return result;
+ } else {
+ throw new Error("There should be a user! Why does Dash think there isn't one?");
+ }
+ });
+ }
+
+ public static async loadUserDocument({ id, email }: { id: string, email: string }) {
+ this.curr_id = id;
+ Doc.CurrentUserEmail = email;
+ await rp.get(Utils.prepend("/getUserDocumentId")).then(id => {
+ if (id && id !== "guest") {
+ return DocServer.GetRefField(id).then(async field =>
+ Doc.SetUserDoc(await this.updateUserDocument(field instanceof Doc ? field : new Doc(id, true))));
+ } else {
+ throw new Error("There should be a user id! Why does Dash think there isn't one?");
+ }
+ });
+ }
+}
+
+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
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index b3295ece0..e46225b4a 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -3,15 +3,15 @@ import { DocumentView } from "../views/nodes/DocumentView";
import { UndoManager } from "./UndoManager";
import * as interpreter from "words-to-numbers";
import { DocumentType } from "../documents/DocumentTypes";
-import { Doc, Opt } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
+import { Doc, Opt } from "../../fields/Doc";
+import { List } from "../../fields/List";
import { Docs } from "../documents/Documents";
import { CollectionViewType } from "../views/collections/CollectionView";
-import { Cast, CastCtor } from "../../new_fields/Types";
-import { listSpec } from "../../new_fields/Schema";
-import { AudioField, ImageField } from "../../new_fields/URLField";
+import { Cast, CastCtor } from "../../fields/Types";
+import { listSpec } from "../../fields/Schema";
+import { AudioField, ImageField } from "../../fields/URLField";
import { Utils } from "../../Utils";
-import { RichTextField } from "../../new_fields/RichTextField";
+import { RichTextField } from "../../fields/RichTextField";
import { DictationOverlay } from "../views/DictationOverlay";
/**
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 1ba6f0248..ab087335e 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,7 @@
import { action, computed, observable } from 'mobx';
-import { Doc, DocListCastAsync, DocListCast, Opt } from '../../new_fields/Doc';
-import { Id } from '../../new_fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../new_fields/Types';
+import { Doc, DocListCastAsync, DocListCast, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { CollectionView } from '../views/collections/CollectionView';
import { DocumentView, DocFocusFunc } from '../views/nodes/DocumentView';
@@ -94,37 +94,29 @@ export class DocumentManager {
// heuristic to return the "best" documents first:
// choose an exact match over an alias match
// choose documents that have a PanelWidth() over those that don't (the treeview documents have no panelWidth)
- docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view));
- docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view));
- docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
- docViews.map(view => !view.props.Document.presBox && view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
+ docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document === toFind && toReturn.push(view));
+ docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document === toFind && toReturn.push(view));
+ docViews.map(view => view.props.PanelWidth() > 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
+ docViews.map(view => view.props.PanelWidth() <= 1 && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
return toReturn;
}
@computed
public get LinkedDocumentViews() {
- const pairs = DocumentManager.Instance.DocumentViews
- //.filter(dv => (dv.isSelected() || Doc.IsBrushed(dv.props.Document))) // draw links from DocumentViews that are selected or brushed OR
- // || DocumentManager.Instance.DocumentViews.some(dv2 => { // Documentviews which
- // const rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors
- // const init = (dv2.isSelected() || Doc.IsBrushed(dv2.props.Document)) && dv2.Document.type !== DocumentType.AUDIO; // on a view that is selected or brushed
- // return init && rest;
- // }
- // )
- .reduce((pairs, dv) => {
- const linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
- pairs.push(...linksList.reduce((pairs, link) => {
- const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
- linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
- if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) {
- pairs.push({ a: dv, b: docView1, l: link });
- }
- });
- return pairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
+ const pairs = DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => {
+ const linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
+ pairs.push(...linksList.reduce((pairs, link) => {
+ const linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
+ linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
+ if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) {
+ pairs.push({ a: dv, b: docView1, l: link });
+ }
+ });
return pairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
+ }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
+ return pairs;
+ }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
return pairs;
}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d91eb60ef..2a9c1633a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,16 +1,17 @@
import { action, observable, runInAction } from "mobx";
-import { DateField } from "../../new_fields/DateField";
-import { Doc, Field, Opt } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
-import { PrefetchProxy } from "../../new_fields/Proxy";
-import { listSpec } from "../../new_fields/Schema";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
-import { ScriptField } from "../../new_fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../new_fields/Types";
+import { DateField } from "../../fields/DateField";
+import { Doc, Field, Opt } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { PrefetchProxy } from "../../fields/Proxy";
+import { listSpec } from "../../fields/Schema";
+import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
+import { ScriptField } from "../../fields/ScriptField";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
import { emptyFunction } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
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 function SetupDrag(
@@ -49,7 +50,7 @@ export function SetupDrag(
if (e.shiftKey) {
e.persist();
const dragDoc = await docFunc();
- dragDoc && DragManager.Vals.Instance.StartWindowDrag?.({
+ dragDoc && DragManager.StartWindowDrag?.({
pageX: e.pageX,
pageY: e.pageY,
preventDefault: emptyFunction,
@@ -66,19 +67,7 @@ export function SetupDrag(
export namespace DragManager {
let dragDiv: HTMLDivElement;
- export class Vals {
- static Instance: Vals = new Vals();
- @observable public IsDragging: boolean = false;
- @observable public horizSnapLines: number[] = [];
- @observable public vertSnapLines: number[] = [];
- public StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
- public SetIsDragging(dragging: boolean) { runInAction(() => this.IsDragging = dragging); }
- public GetIsDragging() { return this.IsDragging; }
- public clearSnapLines() {
- this.vertSnapLines.length = 0;
- this.horizSnapLines.length = 0;
- }
- }
+ export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
export function Root() {
const root = document.getElementById("root");
@@ -88,8 +77,8 @@ export namespace DragManager {
return root;
}
export let AbortDrag: () => void = emptyFunction;
- export type MoveFunction = (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
- export type RemoveFunction = (document: Doc) => boolean;
+ export type MoveFunction = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+ export type RemoveFunction = (document: Doc | Doc[]) => boolean;
export interface DragDropDisposer { (): void; }
export interface DragOptions {
@@ -187,7 +176,7 @@ export namespace DragManager {
element: HTMLElement,
dropFunc: (e: Event, de: DropEvent) => void,
doc?: Doc,
- preDropFunc?: (e: Event, de: DropEvent) => void,
+ preDropFunc?: (e: Event, de: DropEvent, targetAction: dropActionType) => void,
): DragDropDisposer {
if ("canDrop" in element.dataset) {
throw new Error(
@@ -198,10 +187,7 @@ export namespace DragManager {
const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
const preDropHandler = (e: Event) => {
const de = (e as CustomEvent<DropEvent>).detail;
- if (de.complete.docDragData && doc?.targetDropAction) {
- de.complete.docDragData.dropAction = StrCast(doc.targetDropAction) as dropActionType;
- }
- preDropFunc?.(e, de);
+ preDropFunc?.(e, de, StrCast(doc?.targetDropAction) as dropActionType);
};
element.addEventListener("dashOnDrop", handler);
doc && element.addEventListener("dashPreDrop", preDropHandler);
@@ -267,10 +253,43 @@ export namespace DragManager {
}
export function SetSnapLines(horizLines: number[], vertLines: number[]) {
- DragManager.Vals.Instance.horizSnapLines.push(...horizLines);
- DragManager.Vals.Instance.vertSnapLines.push(...vertLines);
+ SnappingManager.setSnapLines(horizLines, vertLines);
}
+ export function snapDragAspect(dragPt: number[], snapAspect: number) {
+ let closest = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10);
+ let near = dragPt;
+ const intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, dragx: number, dragy: number) => {
+ if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return undefined; // Check if none of the lines are of length 0
+ const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
+ if (denominator === 0) return undefined; // Lines are parallel
+ const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
+ // let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
+ //if (ua < 0 || ua > 1 || ub < 0 || ub > 1) return undefined; // is the intersection along the segments
+
+ // Return a object with the x and y coordinates of the intersection
+ const x = x1 + ua * (x2 - x1);
+ const y = y1 + ua * (y2 - y1);
+ const dist = Math.sqrt((dragx - x) * (dragx - x) + (dragy - y) * (dragy - y));
+ return { pt: [x, y], dist };
+ };
+ SnappingManager.vertSnapLines().forEach((xCoord, i) => {
+ const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, xCoord, -1, xCoord, 1, dragPt[0], dragPt[1]);
+ if (pt && pt.dist < closest) {
+ closest = pt.dist;
+ near = pt.pt;
+ }
+ });
+ SnappingManager.horizSnapLines().forEach((yCoord, i) => {
+ const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, -1, yCoord, 1, yCoord, dragPt[0], dragPt[1]);
+ if (pt && pt.dist < closest) {
+ closest = pt.dist;
+ near = pt.pt;
+ }
+ });
+ return { thisX: near[0], thisY: near[1] };
+ }
+ // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line
export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
const snapThreshold = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10);
const snapVal = (pts: number[], drag: number, snapLines: number[]) => {
@@ -284,10 +303,9 @@ export namespace DragManager {
}
return drag;
};
-
return {
- thisX: snapVal([xFromLeft, xFromRight], e.pageX, DragManager.Vals.Instance.vertSnapLines),
- thisY: snapVal([yFromTop, yFromBottom], e.pageY, DragManager.Vals.Instance.horizSnapLines)
+ thisX: snapVal([xFromLeft, xFromRight], e.pageX, SnappingManager.vertSnapLines()),
+ thisY: snapVal([yFromTop, yFromBottom], e.pageY, SnappingManager.horizSnapLines())
};
}
export let docsBeingDragged: Doc[] = [];
@@ -299,7 +317,7 @@ export namespace DragManager {
dragDiv.style.pointerEvents = "none";
DragManager.Root().appendChild(dragDiv);
}
- DragManager.Vals.Instance.SetIsDragging(true);
+ SnappingManager.SetIsDragging(true);
const scaleXs: number[] = [];
const scaleYs: number[] = [];
const xs: number[] = [];
@@ -388,7 +406,7 @@ export namespace DragManager {
}
AbortDrag();
finishDrag?.(new DragCompleteEvent(true, dragData));
- DragManager.Vals.Instance.StartWindowDrag?.({
+ DragManager.StartWindowDrag?.({
pageX: e.pageX,
pageY: e.pageY,
preventDefault: emptyFunction,
@@ -415,19 +433,19 @@ export namespace DragManager {
const endDrag = action(() => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- DragManager.Vals.Instance.clearSnapLines();
+ SnappingManager.clearSnapLines();
});
AbortDrag = () => {
hideDragShowOriginalElements();
- DragManager.Vals.Instance.SetIsDragging(false);
+ SnappingManager.SetIsDragging(false);
options?.dragComplete?.(new DragCompleteEvent(true, dragData));
endDrag();
};
const upHandler = (e: PointerEvent) => {
hideDragShowOriginalElements();
dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
- DragManager.Vals.Instance.SetIsDragging(false);
+ SnappingManager.SetIsDragging(false);
endDrag();
options?.dragComplete?.(new DragCompleteEvent(false, dragData));
};
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index d6db882b8..752c1cfc5 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -1,12 +1,12 @@
import { DragManager } from "./DragManager";
-import { Doc, DocListCast, Opt } from "../../new_fields/Doc";
+import { Doc, DocListCast, Opt } from "../../fields/Doc";
import { DocumentType } from "../documents/DocumentTypes";
-import { ObjectField } from "../../new_fields/ObjectField";
-import { StrCast } from "../../new_fields/Types";
+import { ObjectField } from "../../fields/ObjectField";
+import { StrCast } from "../../fields/Types";
import { Docs } from "../documents/Documents";
-import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
-import { RichTextField } from "../../new_fields/RichTextField";
-import { ImageField } from "../../new_fields/URLField";
+import { ScriptField, ComputedField } from "../../fields/ScriptField";
+import { RichTextField } from "../../fields/RichTextField";
+import { ImageField } from "../../fields/URLField";
import { Scripting } from "./Scripting";
//
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 2c53d7e52..7b7d4b835 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,4 +1,4 @@
-import { Doc } from "../../new_fields/Doc";
+import { Doc } from "../../fields/Doc";
import { DocServer } from "../DocServer";
import { MainView } from "../views/MainView";
import * as qs from 'query-string';
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 438904688..1e8f07049 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -1,6 +1,6 @@
import "fs";
import React = require("react");
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
+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";
@@ -12,12 +12,12 @@ import { observer } from "mobx-react";
import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from "./ImportMetadataEntry";
import { Utils } from "../../../Utils";
import { DocumentManager } from "../DocumentManager";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { Cast, BoolCast, NumCast } from "../../../new_fields/Types";
-import { listSpec } from "../../../new_fields/Schema";
+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 "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import "./DirectoryImportBox.scss";
import { Networking } from "../../Network";
import { BatchedArray } from "array-batcher";
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index c8d1530b3..072e5f58a 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -1,9 +1,9 @@
-import { Doc } from "../../../new_fields/Doc";
-import { ImageField } from "../../../new_fields/URLField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { ImageField } from "../../../fields/URLField";
+import { Cast, StrCast } from "../../../fields/Types";
import { Docs } from "../../documents/Documents";
import { Networking } from "../../Network";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
import { Utils } from "../../../Utils";
export namespace ImageUtils {
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index 8e1c50bea..dcb94e2e0 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx
@@ -5,8 +5,8 @@ import { action, computed } from "mobx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { library } from '@fortawesome/fontawesome-svg-core';
-import { Doc } from "../../../new_fields/Doc";
-import { StrCast, BoolCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { StrCast, BoolCast } from "../../../fields/Types";
interface KeyValueProps {
Document: Doc;
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index b1f136430..3a5345c80 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -87,15 +87,17 @@ export namespace InteractionUtils {
return myTouches;
}
- export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: number) {
+ export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string) {
const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
return (
<polyline
points={pts}
style={{
fill: "none",
- stroke: color,
- strokeWidth: width
+ stroke: color ?? "rgb(0, 0, 0)",
+ strokeWidth: parseInt(width),
+ strokeLinejoin: "round",
+ strokeLinecap: "round"
}}
/>
);
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index e236c7f47..8e6ccf098 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,7 @@
-import { Doc, DocListCast } from "../../new_fields/Doc";
-import { List } from "../../new_fields/List";
-import { listSpec } from "../../new_fields/Schema";
-import { Cast, StrCast } from "../../new_fields/Types";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { listSpec } from "../../fields/Schema";
+import { Cast, StrCast } from "../../fields/Types";
import { Docs } from "../documents/Documents";
import { Scripting } from "./Scripting";
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 12628273b..ab577315c 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -9,7 +9,7 @@ export { ts };
// @ts-ignore
import * as typescriptlib from '!!raw-loader!./type_decls.d';
-import { Doc, Field } from '../../new_fields/Doc';
+import { Doc, Field } from '../../fields/Doc';
export interface ScriptSucccess {
success: true;
@@ -136,7 +136,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
if (batch) {
batch.end();
}
- onError && onError(error);
+ onError?.(error);
return { success: false, error, result: errorVal };
}
};
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 6501da34a..5679c0a14 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -1,7 +1,7 @@
import * as rp from 'request-promise';
import { DocServer } from '../DocServer';
-import { Doc } from '../../new_fields/Doc';
-import { Id } from '../../new_fields/FieldSymbols';
+import { Doc } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
import { Utils } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index d509168b6..bd743c28e 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,13 +1,14 @@
import { observable, action, runInAction, ObservableMap } from "mobx";
-import { Doc } from "../../new_fields/Doc";
+import { Doc } from "../../fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { computedFn } from "mobx-utils";
-import { List } from "../../new_fields/List";
+import { List } from "../../fields/List";
export namespace SelectionManager {
class Manager {
+ @observable IsDragging: boolean = false;
SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
@action
@@ -53,6 +54,8 @@ export namespace SelectionManager {
manager.SelectDoc(docView, ctrlPressed);
}
+ export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
+ export function GetIsDragging() { return manager.IsDragging; }
// computed functions, such as used in IsSelected generate errors if they're called outside of a
// reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature
// to avoid unnecessary mobx invalidations when running inside a reaction.
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index 1f6b939d3..ad0309fa7 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,5 +1,5 @@
import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from "serializr";
-import { Field } from "../../new_fields/Doc";
+import { Field } from "../../fields/Doc";
import { ClientUtils } from "./ClientUtils";
let serializing = 0;
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index ff0b22381..0e15197c4 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -8,6 +8,8 @@ import { SelectionManager } from "./SelectionManager";
import "./SettingsManager.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Networking } from "../Network";
+import { CurrentUserUtils } from "./CurrentUserUtils";
+import { Utils } from "../../Utils";
library.add(fa.faWindowClose);
@@ -90,6 +92,9 @@ export default class SettingsManager extends React.Component<{}> {
<div className="settings-type">
<button onClick={this.onClick} value="password">reset password</button>
<button onClick={this.onClick} value="data">reset data</button>
+ <button onClick={() => window.location.assign(Utils.prepend("/logout"))}>
+ {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
+ </button>
</div>
{this.settingsContent === "password" ?
<div className="settings-content">
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3ce6de80d..dc67145fc 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,13 +1,13 @@
import { observable, runInAction, action } from "mobx";
import * as React from "react";
import MainViewModal from "../views/MainViewModal";
-import { Doc, Opt, DocCastAsync } from "../../new_fields/Doc";
+import { Doc, Opt, DocCastAsync } from "../../fields/Doc";
import { DocServer } from "../DocServer";
-import { Cast, StrCast } from "../../new_fields/Types";
+import { Cast, StrCast } from "../../fields/Types";
import * as RequestPromise from "request-promise";
import { Utils } from "../../Utils";
import "./SharingManager.scss";
-import { Id } from "../../new_fields/FieldSymbols";
+import { Id } from "../../fields/FieldSymbols";
import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { library } from '@fortawesome/fontawesome-svg-core';
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
new file mode 100644
index 000000000..fc07e8ab4
--- /dev/null
+++ b/src/client/util/SnappingManager.ts
@@ -0,0 +1,29 @@
+import { observable, action, runInAction } from "mobx";
+
+export namespace SnappingManager {
+
+ class Manager {
+ @observable IsDragging: boolean = false;
+ @observable public horizSnapLines: number[] = [];
+ @observable public vertSnapLines: number[] = [];
+ @action public clearSnapLines() {
+ this.vertSnapLines = [];
+ this.horizSnapLines = [];
+ }
+ @action public setSnapLines(horizLines: number[], vertLines: number[]) {
+ this.horizSnapLines = horizLines;
+ this.vertSnapLines = vertLines;
+ }
+ }
+
+ const manager = new Manager();
+
+ export function clearSnapLines() { manager.clearSnapLines(); }
+ export function setSnapLines(horizLines: number[], vertLines: number[]) { manager.setSnapLines(horizLines, vertLines); }
+ export function horizSnapLines() { return manager.horizSnapLines; }
+ export function vertSnapLines() { return manager.vertSnapLines; }
+
+ export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
+ export function GetIsDragging() { return manager.IsDragging; }
+}
+