aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndy Rickert <andrew_rickert@brown.edu>2020-06-03 16:40:09 -0400
committerAndy Rickert <andrew_rickert@brown.edu>2020-06-03 16:40:09 -0400
commit954948ddd511578af4ca2c50c960765a5a7bc637 (patch)
tree16fafd254a5db95d5c39838d4313d7ddf59753af /src
parent6d8d3c00587c43ae61392db4fe6915ee492c2e4a (diff)
parent9588e56079f7e4ab98da1849f44996656649bc06 (diff)
merge
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/ClientRecommender.tsx16
-rw-r--r--src/client/DocServer.ts12
-rw-r--r--src/client/apis/GoogleAuthenticationManager.tsx2
-rw-r--r--src/client/apis/IBM_Recommender.ts2
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts2
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts12
-rw-r--r--src/client/apis/youtube/YoutubeBox.tsx4
-rw-r--r--src/client/cognitive_services/CognitiveServices.ts16
-rw-r--r--src/client/documents/DocumentTypes.ts1
-rw-r--r--src/client/documents/Documents.ts68
-rw-r--r--src/client/util/CurrentUserUtils.ts (renamed from src/server/authentication/models/current_user_utils.ts)59
-rw-r--r--src/client/util/DictationManager.ts12
-rw-r--r--src/client/util/DocumentManager.ts60
-rw-r--r--src/client/util/DragManager.ts26
-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.ts2
-rw-r--r--src/client/util/SearchUtil.ts4
-rw-r--r--src/client/util/SelectionManager.ts6
-rw-r--r--src/client/util/SerializationHelper.ts4
-rw-r--r--src/client/util/SettingsManager.tsx2
-rw-r--r--src/client/util/SharingManager.tsx6
-rw-r--r--src/client/views/DocComponent.tsx26
-rw-r--r--src/client/views/DocumentButtonBar.tsx8
-rw-r--r--src/client/views/DocumentDecorations.scss8
-rw-r--r--src/client/views/DocumentDecorations.tsx40
-rw-r--r--src/client/views/EditableView.tsx4
-rw-r--r--src/client/views/GestureOverlay.tsx38
-rw-r--r--src/client/views/GlobalKeyHandler.ts14
-rw-r--r--src/client/views/InkingControl.tsx21
-rw-r--r--src/client/views/InkingStroke.tsx12
-rw-r--r--src/client/views/Main.tsx8
-rw-r--r--src/client/views/MainView.tsx141
-rw-r--r--src/client/views/MainViewNotifs.tsx2
-rw-r--r--src/client/views/MetadataEntryMenu.tsx2
-rw-r--r--src/client/views/OverlayView.tsx52
-rw-r--r--src/client/views/Palette.tsx4
-rw-r--r--src/client/views/PreviewCursor.tsx11
-rw-r--r--src/client/views/RecommendationsBox.tsx8
-rw-r--r--src/client/views/ScriptBox.tsx6
-rw-r--r--src/client/views/SearchDocBox.tsx6
-rw-r--r--src/client/views/TemplateMenu.tsx24
-rw-r--r--src/client/views/animationtimeline/Keyframe.tsx76
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx8
-rw-r--r--src/client/views/animationtimeline/Track.tsx12
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx15
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx32
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx10
-rw-r--r--src/client/views/collections/CollectionMapView.tsx29
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx22
-rw-r--r--src/client/views/collections/CollectionPileView.scss3
-rw-r--r--src/client/views/collections/CollectionPileView.tsx63
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx8
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx6
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx14
-rw-r--r--src/client/views/collections/CollectionStackingView.scss42
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx21
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx18
-rw-r--r--src/client/views/collections/CollectionStaffView.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx65
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx12
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx25
-rw-r--r--src/client/views/collections/CollectionView.tsx47
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss60
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx212
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx18
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx12
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx192
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx149
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx10
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx10
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx4
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx4
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx4
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx4
-rw-r--r--src/client/views/linking/LinkEditor.tsx6
-rw-r--r--src/client/views/linking/LinkMenu.tsx2
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx6
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx4
-rw-r--r--src/client/views/nodes/AudioBox.tsx16
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx88
-rw-r--r--src/client/views/nodes/ColorBox.tsx12
-rw-r--r--src/client/views/nodes/ComparisonBox.scss93
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx114
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx6
-rw-r--r--src/client/views/nodes/DocHolderBox.tsx12
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx13
-rw-r--r--src/client/views/nodes/DocumentIcon.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.scss1
-rw-r--r--src/client/views/nodes/DocumentView.tsx74
-rw-r--r--src/client/views/nodes/FaceRectangles.tsx6
-rw-r--r--src/client/views/nodes/FieldView.tsx10
-rw-r--r--src/client/views/nodes/FontIconBox.tsx8
-rw-r--r--src/client/views/nodes/ImageBox.tsx27
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx14
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx6
-rw-r--r--src/client/views/nodes/LabelBox.tsx13
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx12
-rw-r--r--src/client/views/nodes/LinkBox.tsx6
-rw-r--r--src/client/views/nodes/PDFBox.tsx12
-rw-r--r--src/client/views/nodes/PresBox.tsx47
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx19
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx10
-rw-r--r--src/client/views/nodes/SliderBox.tsx8
-rw-r--r--src/client/views/nodes/VideoBox.tsx80
-rw-r--r--src/client/views/nodes/WebBox.scss52
-rw-r--r--src/client/views/nodes/WebBox.tsx229
-rw-r--r--src/client/views/nodes/formattedText/DashDocCommentView.tsx16
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx10
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx12
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx48
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx8
-rw-r--r--src/client/views/nodes/formattedText/ImageResizeView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts24
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx6
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts10
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx16
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts2
-rw-r--r--src/client/views/pdf/Annotation.tsx8
-rw-r--r--src/client/views/pdf/PDFMenu.tsx2
-rw-r--r--src/client/views/pdf/PDFViewer.scss4
-rw-r--r--src/client/views/pdf/PDFViewer.tsx39
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx35
-rw-r--r--src/client/views/search/CheckBox.tsx1
-rw-r--r--src/client/views/search/FilterBox.tsx6
-rw-r--r--src/client/views/search/SearchBox.tsx21
-rw-r--r--src/client/views/search/SearchItem.tsx14
-rw-r--r--src/client/views/webcam/WebCamLogic.js5
-rw-r--r--src/debug/Repl.tsx9
-rw-r--r--src/debug/Test.tsx2
-rw-r--r--src/debug/Viewer.tsx21
-rw-r--r--src/fields/CursorField.ts (renamed from src/new_fields/CursorField.ts)0
-rw-r--r--src/fields/DateField.ts (renamed from src/new_fields/DateField.ts)0
-rw-r--r--src/fields/Doc.ts (renamed from src/new_fields/Doc.ts)93
-rw-r--r--src/fields/FieldSymbols.ts (renamed from src/new_fields/FieldSymbols.ts)0
-rw-r--r--src/fields/HtmlField.ts (renamed from src/new_fields/HtmlField.ts)0
-rw-r--r--src/fields/IconField.ts (renamed from src/new_fields/IconField.ts)0
-rw-r--r--src/fields/InkField.ts (renamed from src/new_fields/InkField.ts)0
-rw-r--r--src/fields/List.ts (renamed from src/new_fields/List.ts)0
-rw-r--r--src/fields/ListSpec.ts (renamed from src/new_fields/ListSpec.ts)0
-rw-r--r--src/fields/ObjectField.ts (renamed from src/new_fields/ObjectField.ts)0
-rw-r--r--src/fields/PresField.ts (renamed from src/new_fields/PresField.ts)0
-rw-r--r--src/fields/Proxy.ts (renamed from src/new_fields/Proxy.ts)0
-rw-r--r--src/fields/RefField.ts (renamed from src/new_fields/RefField.ts)0
-rw-r--r--src/fields/RichTextField.ts (renamed from src/new_fields/RichTextField.ts)0
-rw-r--r--src/fields/RichTextUtils.ts (renamed from src/new_fields/RichTextUtils.ts)0
-rw-r--r--src/fields/Schema.ts (renamed from src/new_fields/Schema.ts)0
-rw-r--r--src/fields/SchemaHeaderField.ts (renamed from src/new_fields/SchemaHeaderField.ts)0
-rw-r--r--src/fields/ScriptField.ts (renamed from src/new_fields/ScriptField.ts)33
-rw-r--r--src/fields/Types.ts (renamed from src/new_fields/Types.ts)0
-rw-r--r--src/fields/URLField.ts (renamed from src/new_fields/URLField.ts)0
-rw-r--r--src/fields/documentSchemas.ts (renamed from src/new_fields/documentSchemas.ts)7
-rw-r--r--src/fields/util.ts (renamed from src/new_fields/util.ts)15
-rw-r--r--src/mobile/ImageUpload.tsx19
-rw-r--r--src/mobile/MobileInkOverlay.tsx6
-rw-r--r--src/mobile/MobileInterface.tsx16
-rw-r--r--src/pen-gestures/GestureUtils.ts6
-rw-r--r--src/scraping/buxton/final/BuxtonImporter.ts90
-rw-r--r--src/scraping/buxton/final/assets/pdfs/3DCad_Brochure.pdfbin0 -> 107790 bytes
-rw-r--r--src/server/ApiManagers/ApiManager.ts2
-rw-r--r--src/server/ApiManagers/DeleteManager.ts14
-rw-r--r--src/server/ApiManagers/DownloadManager.ts2
-rw-r--r--src/server/ApiManagers/GeneralGoogleManager.ts2
-rw-r--r--src/server/ApiManagers/GooglePhotosManager.ts2
-rw-r--r--src/server/ApiManagers/PDFManager.ts43
-rw-r--r--src/server/ApiManagers/SearchManager.ts3
-rw-r--r--src/server/ApiManagers/UploadManager.ts74
-rw-r--r--src/server/ApiManagers/UserManager.ts2
-rw-r--r--src/server/ApiManagers/UtilManager.ts3
-rw-r--r--src/server/DashSession/DashSessionAgent.ts2
-rw-r--r--src/server/DashUploadUtils.ts14
-rw-r--r--src/server/Message.ts2
-rw-r--r--src/server/Recommender.ts6
-rw-r--r--src/server/RouteManager.ts17
-rw-r--r--src/server/apis/google/CredentialsLoader.ts67
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts12
-rw-r--r--src/server/apis/google/google_project_credentials.json (renamed from src/server/credentials/google_project_credentials.json)0
-rw-r--r--src/server/apis/youtube/youtubeApiSample.js22
-rw-r--r--src/server/authentication/AuthenticationManager.ts (renamed from src/server/authentication/controllers/user_controller.ts)8
-rw-r--r--src/server/authentication/DashUserModel.ts (renamed from src/server/authentication/models/user_model.ts)4
-rw-r--r--src/server/authentication/Passport.ts (renamed from src/server/authentication/config/passport.ts)2
-rw-r--r--src/server/credentials/CredentialsLoader.ts29
-rw-r--r--src/server/database.ts80
-rw-r--r--src/server/index.ts65
-rw-r--r--src/server/remapUrl.ts3
-rw-r--r--src/server/server_Initialization.ts57
-rw-r--r--src/server/websocket.ts (renamed from src/server/Websocket/Websocket.ts)59
198 files changed, 2675 insertions, 1570 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index bcb215804..ef5002bec 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -43,7 +43,7 @@ export namespace Utils {
}
/**
- * A convenience method. Prepends the full path (i.e. http://localhost:1050) to the
+ * A convenience method. Prepends the full path (i.e. http://localhost:<port>) to the
* requested extension
* @param extension the specified sub-path to append to the window origin
*/
diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
index 537e331ab..d18669b02 100644
--- a/src/client/ClientRecommender.tsx
+++ b/src/client/ClientRecommender.tsx
@@ -1,6 +1,6 @@
-import { Doc, FieldResult } from "../new_fields/Doc";
-import { StrCast, Cast } from "../new_fields/Types";
-import { List } from "../new_fields/List";
+import { Doc, FieldResult } from "../fields/Doc";
+import { StrCast, Cast } from "../fields/Types";
+import { List } from "../fields/List";
import { CognitiveServices, Confidence, Tag, Service } from "./cognitive_services/CognitiveServices";
import React = require("react");
import { observer } from "mobx-react";
@@ -11,11 +11,11 @@ import { observable, action, computed, reaction } from "mobx";
// var https = require('https');
import "./ClientRecommender.scss";
import { JSXElement } from "babel-types";
-import { RichTextField } from "../new_fields/RichTextField";
-import { ToPlainText } from "../new_fields/FieldSymbols";
-import { listSpec } from "../new_fields/Schema";
-import { ComputedField } from "../new_fields/ScriptField";
-import { ImageField } from "../new_fields/URLField";
+import { RichTextField } from "../fields/RichTextField";
+import { ToPlainText } from "../fields/FieldSymbols";
+import { listSpec } from "../fields/Schema";
+import { ComputedField } from "../fields/ScriptField";
+import { ImageField } from "../fields/URLField";
import { KeyphraseQueryView } from "./views/KeyphraseQueryView";
import { Networking } from "./Network";
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 34ef502ad..c6b3fa61f 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,10 +1,10 @@
-import * as OpenSocket from 'socket.io-client';
+import * as io from 'socket.io-client';
import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
-import { Opt, Doc } from '../new_fields/Doc';
+import { Opt, Doc } from '../fields/Doc';
import { Utils, emptyFunction } from '../Utils';
import { SerializationHelper } from './util/SerializationHelper';
-import { RefField } from '../new_fields/RefField';
-import { Id, HandleUpdate } from '../new_fields/FieldSymbols';
+import { RefField } from '../fields/RefField';
+import { Id, HandleUpdate } from '../fields/FieldSymbols';
import GestureOverlay from './views/GestureOverlay';
import MobileInkOverlay from '../mobile/MobileInkOverlay';
import { runInAction } from 'mobx';
@@ -108,7 +108,9 @@ export namespace DocServer {
export function init(protocol: string, hostname: string, port: number, identifier: string) {
_cache = {};
GUID = identifier;
- _socket = OpenSocket(`${protocol}//${hostname}:${port}`);
+ protocol = protocol.startsWith("https") ? "wss" : "ws";
+ _socket = io.connect(`${protocol}://${hostname}:${port}`);
+ // io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
_GetCachedRefField = _GetCachedRefFieldImpl;
_GetRefField = _GetRefFieldImpl;
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index 22d6fb582..bf4469aeb 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -2,7 +2,7 @@ import { observable, action, reaction, runInAction, IReactionDisposer } from "mo
import { observer } from "mobx-react";
import * as React from "react";
import MainViewModal from "../views/MainViewModal";
-import { Opt } from "../../new_fields/Doc";
+import { Opt } from "../../fields/Doc";
import { Networking } from "../Network";
import "./GoogleAuthenticationManager.scss";
import { Scripting } from "../util/Scripting";
diff --git a/src/client/apis/IBM_Recommender.ts b/src/client/apis/IBM_Recommender.ts
index 4e1c541c8..480b9cb1c 100644
--- a/src/client/apis/IBM_Recommender.ts
+++ b/src/client/apis/IBM_Recommender.ts
@@ -1,4 +1,4 @@
-// import { Opt } from "../../new_fields/Doc";
+// import { Opt } from "../../fields/Doc";
// const NaturalLanguageUnderstandingV1 = require('ibm-watson/natural-language-understanding/v1');
// const { IamAuthenticator } = require('ibm-watson/auth');
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 2f3cac8d3..551dca073 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -1,5 +1,5 @@
import { docs_v1 } from "googleapis";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt } from "../../../fields/Doc";
import { isArray } from "util";
import { EditorState } from "prosemirror-state";
import { Networking } from "../../Network";
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 1e4c120bc..fef71ffeb 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,11 +1,11 @@
import { AssertionError } from "assert";
import { EditorState } from "prosemirror-state";
-import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { RichTextUtils } from "../../../new_fields/RichTextUtils";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { ImageField } from "../../../new_fields/URLField";
+import { Doc, DocListCastAsync, Opt } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { RichTextField } from "../../../fields/RichTextField";
+import { RichTextUtils } from "../../../fields/RichTextUtils";
+import { Cast, StrCast } from "../../../fields/Types";
+import { ImageField } from "../../../fields/URLField";
import { MediaItem, NewMediaItemResult } from "../../../server/apis/google/SharedTypes";
import { Utils } from "../../../Utils";
import { Docs, DocumentOptions } from "../../documents/Documents";
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index 1575e53fc..ce7f49e64 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -1,7 +1,7 @@
import { action, observable, runInAction } from 'mobx';
import { observer } from "mobx-react";
-import { Doc, DocListCastAsync } from "../../../new_fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCastAsync } from "../../../fields/Doc";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs } from "../../documents/Documents";
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 8c63ae906..b816d1617 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -1,12 +1,12 @@
import * as request from "request-promise";
-import { Doc, Field } from "../../new_fields/Doc";
-import { Cast } from "../../new_fields/Types";
+import { Doc, Field } from "../../fields/Doc";
+import { Cast } from "../../fields/Types";
import { Docs } from "../documents/Documents";
import { Utils } from "../../Utils";
-import { InkData } from "../../new_fields/InkField";
+import { InkData } from "../../fields/InkField";
import { UndoManager } from "../util/UndoManager";
import requestPromise = require("request-promise");
-import { List } from "../../new_fields/List";
+import { List } from "../../fields/List";
import { ClientRecommender } from "../ClientRecommender";
type APIManager<D> = { converter: BodyConverter<D>, requester: RequestExecutor };
@@ -45,9 +45,13 @@ export enum Confidence {
export namespace CognitiveServices {
const ExecuteQuery = async <D>(service: Service, manager: APIManager<D>, data: D): Promise<any> => {
- const apiKey = process.env[service.toUpperCase()];
+ let apiKey = process.env[service.toUpperCase()];
+ // A HACK FOR A DEMO VIDEO - syip2
+ if (service === "handwriting") {
+ apiKey = "61088486d76c4b12ba578775a5f55422";
+ }
if (!apiKey) {
- console.log(`No API key found for ${service}: ensure index.ts has access to a .env file in your root directory.`);
+ console.log(`No API key found for ${service}: ensure youe root directory has .env file with _CLIENT_${service.toUpperCase()}.`);
return undefined;
}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 36d3e1c52..90e6765b0 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -32,6 +32,7 @@ export enum DocumentType {
YOUTUBE = "youtube", // youtube directory (view of you tube search results)
DOCHOLDER = "docholder", // nested document (view of a document)
SEARCHITEM= "searchitem",
+ COMPARISON = "comparison", // before/after view with slider (view of 2 images)
LINKDB = "linkdb", // database of links ??? why do we have this
RECOMMENDATION = "recommendation", // view of a recommendation
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f0f45bd34..0016ae594 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -9,14 +9,14 @@ import { ScriptingBox } from "../views/nodes/ScriptingBox";
import { VideoBox } from "../views/nodes/VideoBox";
import { WebBox } from "../views/nodes/WebBox";
import { OmitKeys, JSONUtils, Utils } from "../../Utils";
-import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../new_fields/Doc";
-import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField";
-import { HtmlField } from "../../new_fields/HtmlField";
-import { List } from "../../new_fields/List";
-import { Cast, NumCast, StrCast } from "../../new_fields/Types";
+import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../fields/Doc";
+import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../fields/URLField";
+import { HtmlField } from "../../fields/HtmlField";
+import { List } from "../../fields/List";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
import { DocServer } from "../DocServer";
import { dropActionType } from "../util/DragManager";
-import { DateField } from "../../new_fields/DateField";
+import { DateField } from "../../fields/DateField";
import { YoutubeBox } from "../apis/youtube/YoutubeBox";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { LinkManager } from "../util/LinkManager";
@@ -26,10 +26,10 @@ import { Scripting } from "../util/Scripting";
import { LabelBox } from "../views/nodes/LabelBox";
import { SliderBox } from "../views/nodes/SliderBox";
import { FontIconBox } from "../views/nodes/FontIconBox";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
import { PresBox } from "../views/nodes/PresBox";
-import { ComputedField, ScriptField } from "../../new_fields/ScriptField";
-import { ProxyField } from "../../new_fields/Proxy";
+import { ComputedField, ScriptField } from "../../fields/ScriptField";
+import { ProxyField } from "../../fields/Proxy";
import { DocumentType } from "./DocumentTypes";
import { RecommendationsBox } from "../views/RecommendationsBox";
import { filterData} from "../views/search/SearchBox";
@@ -44,15 +44,16 @@ import { ColorBox } from "../views/nodes/ColorBox";
import { LinkAnchorBox } from "../views/nodes/LinkAnchorBox";
import { DocHolderBox } from "../views/nodes/DocHolderBox";
import { InkingStroke } from "../views/InkingStroke";
-import { InkField } from "../../new_fields/InkField";
+import { InkField } from "../../fields/InkField";
import { InkingControl } from "../views/InkingControl";
-import { RichTextField } from "../../new_fields/RichTextField";
+import { RichTextField } from "../../fields/RichTextField";
import { extname } from "path";
import { MessageStore } from "../../server/Message";
import { ContextMenuProps } from "../views/ContextMenuItem";
import { ContextMenu } from "../views/ContextMenu";
import { LinkBox } from "../views/nodes/LinkBox";
import { ScreenshotBox } from "../views/nodes/ScreenshotBox";
+import { ComparisonBox } from "../views/nodes/ComparisonBox";
const path = require('path');
export interface DocumentOptions {
@@ -114,10 +115,11 @@ export interface DocumentOptions {
_backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor )
color?: string; // foreground color data doc
_color?: string; // foreground color for each template layout doc (overrides color)
+ _clipWidth?: number; // percent transition from before to after in comparisonBox
caption?: RichTextField;
ignoreClick?: boolean;
lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
- lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
+ _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
isAnnotating?: boolean; // whether we web document is annotation mode where links can't be clicked to allow annotations to be created
opacity?: number;
defaultBackgroundColor?: string;
@@ -129,6 +131,9 @@ export interface DocumentOptions {
curPage?: number;
currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds
displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
+ currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide)
+ lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide)
+ activeFrame?: number; // the active frame of a document in a frame base collection
borderRounding?: string;
boxShadow?: string;
dontRegisterChildViews?: boolean;
@@ -307,6 +312,9 @@ export namespace Docs {
[DocumentType.SCREENSHOT, {
layout: { view: ScreenshotBox, dataField: defaultDataKey },
}],
+ [DocumentType.COMPARISON, {
+ layout: { view: ComparisonBox, dataField: defaultDataKey },
+ }],
]);
// All document prototypes are initialized with at least these values
@@ -434,17 +442,28 @@ export namespace Docs {
parentProto.data = new List<Doc>();
}
if (device) {
- const { __images } = device;
+ const { title, __images, additionalMedia } = device;
delete device.__images;
+ delete device.additionalMedia;
const { ImageDocument, StackingDocument } = Docs.Create;
const constructed = __images.map(({ url, nativeWidth, nativeHeight }) => ({ url: Utils.prepend(url), nativeWidth, nativeHeight }));
- const deviceImages = constructed.map(({ url, nativeWidth, nativeHeight }, i) => ImageDocument(url, {
- title: `image${i}.${extname(url)}`,
- _nativeWidth: nativeWidth,
- _nativeHeight: nativeHeight
- }));
+ const deviceImages = constructed.map(({ url, nativeWidth, nativeHeight }, i) => {
+ const imageDoc = ImageDocument(url, {
+ title: `image${i}.${extname(url)}`,
+ _nativeWidth: nativeWidth,
+ _nativeHeight: nativeHeight
+ });
+ const media = additionalMedia[i];
+ if (media) {
+ for (const key of Object.keys(media)) {
+ imageDoc[`additionalMedia_${key}`] = Utils.prepend(`/files/${key}/buxton/${media[key]}`);
+ }
+ }
+ return imageDoc;
+ });
// the main document we create
- const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true, hero: new ImageField(constructed[0].url) });
+ const doc = StackingDocument(deviceImages, { title, _LODdisable: true, hero: new ImageField(constructed[0].url) });
+ doc.nameAliases = new List<string>([title.toLowerCase()]);
// add the parsed attributes to this main document
Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: Doc.GetProto(doc) } });
Doc.AddDocToList(parentProto, "data", doc);
@@ -505,6 +524,7 @@ export namespace Docs {
const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey);
const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
+ viewDoc.author = Doc.CurrentUserEmail;
viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
return Doc.assign(viewDoc, delegateProps, true);
@@ -564,6 +584,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", options);
}
+ export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), "", { _clipWidth: 50, _backgroundColor: "gray", targetDropAction: "alias", ...options });
+ }
+
export function AudioDocument(url: string, options: DocumentOptions = {}) {
const instance = InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)), options);
Doc.GetProto(instance).backgroundColor = ComputedField.MakeFunction("this._audioState === 'playing' ? 'green':'gray'");
@@ -617,7 +641,7 @@ export namespace Docs {
return doc;
}
- export function InkDocument(color: string, tool: number, strokeWidth: number, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+ export function InkDocument(color: string, tool: number, strokeWidth: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
const I = new Doc();
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString("data");
@@ -645,7 +669,7 @@ export namespace Docs {
}
export function WebDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(new URL(url)) : undefined, { _fitWidth: true, _chromeStatus: url ? "disabled" : "enabled", isAnnotating: true, lockedTransform: true, ...options });
+ return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(new URL(url)) : undefined, { _fitWidth: true, _chromeStatus: url ? "disabled" : "enabled", isAnnotating: true, _lockedTransform: true, ...options });
}
export function HtmlDocument(html: string, options: DocumentOptions = {}) {
@@ -928,7 +952,7 @@ export namespace Docs {
layout = AudioBox.LayoutString;
} else if (field instanceof InkField) {
const { selectedColor, selectedWidth, selectedTool } = InkingControl.Instance;
- created = Docs.Create.InkDocument(selectedColor, selectedTool, Number(selectedWidth), (field).inkData, resolved);
+ created = Docs.Create.InkDocument(selectedColor, selectedTool, selectedWidth, (field).inkData, resolved);
layout = InkingStroke.LayoutString;
} else if (field instanceof List && field[0] instanceof Doc) {
created = Docs.Create.StackingDocument(DocListCast(field), resolved);
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/client/util/CurrentUserUtils.ts
index bc024356d..40e5a3451 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,27 +1,27 @@
-import { action, computed, observable, reaction } from "mobx";
+import { computed, observable, reaction } from "mobx";
import * as rp from 'request-promise';
-import { Utils } from "../../../Utils";
-import { DocServer } from "../../../client/DocServer";
-import { Docs, DocumentOptions } from "../../../client/documents/Documents";
-import { UndoManager } from "../../../client/util/UndoManager";
-import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { ScriptField, ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, PromiseValue, StrCast, NumCast } from "../../../new_fields/Types";
-import { nullAudio, ImageField } from "../../../new_fields/URLField";
-import { DragManager } from "../../../client/util/DragManager";
-import { InkingControl } from "../../../client/views/InkingControl";
-import { Scripting, CompileScript } from "../../../client/util/Scripting";
-import { CollectionViewType } from "../../../client/views/collections/CollectionView";
-import { makeTemplate } from "../../../client/util/DropConverter";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { PrefetchProxy } from "../../../new_fields/Proxy";
-import { FormattedTextBox } from "../../../client/views/nodes/formattedText/FormattedTextBox";
-import { MainView } from "../../../client/views/MainView";
-import { DocumentType } from "../../../client/documents/DocumentTypes";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { DimUnit } from "../../../client/views/collections/collectionMulticolumn/CollectionMulticolumnView";
+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;
@@ -75,8 +75,10 @@ export class CurrentUserUtils {
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} height='calc(100% - {this._headerHeight||75}px)' fieldKey={'text'}/></div>";
+ "<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({
@@ -315,9 +317,10 @@ export class CurrentUserUtils {
{ _width: 250, _height: 250, title: "container" });
}
if (doc.emptyWebpage === undefined) {
- doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600, UseCors: true });
+ doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 600, UseCors: true });
}
return [
+ { title: "Drag a comparison box", label: "Comp", icon: "columns", ignoreClick: true, drag: 'Docs.Create.ComparisonDocument()' },
{ 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" })' },
@@ -610,7 +613,7 @@ export class CurrentUserUtils {
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,2, this.backgroundColor)"),
+ 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
});
}
@@ -694,7 +697,7 @@ export class CurrentUserUtils {
new InkingControl();
doc.title = Doc.CurrentUserEmail;
doc.activePen = doc;
- doc.inkColor = StrCast(doc.backgroundColor, "");
+ 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); //
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..67f2f244c 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;
}
@@ -134,13 +126,13 @@ export class DocumentManager {
finished?.();
}
public jumpToDocument = async (
- targetDoc: Doc,
- willZoom: boolean,
- createViewFunc = DocumentManager.addRightSplit,
- docContext?: Doc,
- linkId?: string,
- closeContextIfNotFound: boolean = false,
- originatingDoc: Opt<Doc> = undefined,
+ targetDoc: Doc, // document to display
+ willZoom: boolean, // whether to zoom doc to take up most of screen
+ createViewFunc = DocumentManager.addRightSplit, // how to create a view of the doc if it doesn't exist
+ docContext?: Doc, // context to load that should contain the target
+ linkId?: string, // link that's being followed
+ closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
+ originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
finished?: () => void
): Promise<void> => {
const getFirstDocView = DocumentManager.Instance.getFirstDocumentView;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d1d7f2a8a..2a9c1633a 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,12 +1,12 @@
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";
@@ -263,16 +263,16 @@ export namespace DragManager {
const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
if (denominator === 0) return undefined; // Lines are parallel
- let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
+ 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
- let x = x1 + ua * (x2 - x1)
- let y = y1 + ua * (y2 - y1)
+ 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 }
- }
+ 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) {
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 8b7b9c9c7..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;
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 77fac3711..15f1f9494 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';
import { StringMap } from 'libxmljs';
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 11d2cafb2..05515e502 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,8 +1,8 @@
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 {
@@ -54,8 +54,6 @@ 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..19b217726 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;
@@ -91,7 +91,7 @@ export function Deserializable(constructor: { new(...args: any[]): any } | strin
if (typeof constructor === "string") {
return Object.assign((ctor: { new(...args: any[]): any }) => {
addToMap(constructor, ctor);
- }, { withFields: (fields: string[]) => Deserializable.withFields(fields, name, afterDeserialize) });
+ }, { withFields: (fields: string[]) => Deserializable.withFields(fields, constructor, afterDeserialize) });
}
addToMap(constructor.name, constructor);
}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index e20434461..0e15197c4 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -8,7 +8,7 @@ import { SelectionManager } from "./SelectionManager";
import "./SettingsManager.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Networking } from "../Network";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { CurrentUserUtils } from "./CurrentUserUtils";
import { Utils } from "../../Utils";
library.add(fa.faWindowClose);
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/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 881e352a6..1ba9fcc32 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,13 +1,14 @@
-import { Doc, Opt, DataSym, DocListCast } from '../../new_fields/Doc';
+import { Doc, Opt, DataSym, DocListCast } from '../../fields/Doc';
import { Touchable } from './Touchable';
import { computed, action, observable } from 'mobx';
-import { Cast, BoolCast, ScriptCast } from '../../new_fields/Types';
-import { listSpec } from '../../new_fields/Schema';
+import { Cast, BoolCast, ScriptCast } from '../../fields/Types';
+import { listSpec } from '../../fields/Schema';
import { InkingControl } from './InkingControl';
-import { InkTool } from '../../new_fields/InkField';
+import { InkTool } from '../../fields/InkField';
import { InteractionUtils } from '../util/InteractionUtils';
-import { List } from '../../new_fields/List';
-import { DateField } from '../../new_fields/DateField';
+import { List } from '../../fields/List';
+import { DateField } from '../../fields/DateField';
+import { ScriptField } from '../../fields/ScriptField';
/// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
@@ -94,6 +95,19 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result;
+ styleFromLayoutString = (scale: number) => {
+ const style: { [key: string]: any } = {};
+ const divKeys = ["width", "height", "background", "top", "position"];
+ const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value
+ return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || "";
+ };
+ divKeys.map((prop: string) => {
+ const p = (this.props as any)[prop] as string;
+ p && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
+ });
+ return style;
+ }
+
protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
_annotationKey: string = "annotations";
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 10d9ec401..a35a8869c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -3,9 +3,9 @@ import { faArrowAltCircleDown, faPhotoVideo, faArrowAltCircleUp, faArrowAltCircl
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../new_fields/Doc";
-import { RichTextField } from '../../new_fields/RichTextField';
-import { NumCast, StrCast, Cast } from "../../new_fields/Types";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { RichTextField } from '../../fields/RichTextField';
+import { NumCast, StrCast, Cast } from "../../fields/Types";
import { emptyFunction, setupMoveUpEvents } from "../../Utils";
import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
import { UndoManager } from "../util/UndoManager";
@@ -121,7 +121,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
dragComplete: dropEv => {
const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
if (this.view0 && linkDoc) {
- Doc.GetProto(linkDoc).linkRelationship = "hyperlink";
+ !linkDoc.linkRelationship && (Doc.GetProto(linkDoc).linkRelationship = "hyperlink");
// we want to allow specific views to handle the link creation in their own way (e.g., rich text makes text hyperlinks)
// the dragged view can regiser a linkDropCallback to be notified that the link was made and to update their data structures
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 15eb537da..a4d4af2f0 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -138,9 +138,10 @@ $linkGap : 3px;
.documentDecorations-contextMenu {
width: 25px;
height: calc(100% + 8px); // 8px for the height of the top resizer bar
- grid-column-start: 1;
+ grid-column-start: 2;
grid-column-end : 2;
pointer-events: all;
+ padding-left: 5px;
}
.documentDecorations-title {
opacity: 1;
@@ -185,9 +186,12 @@ $linkGap : 3px;
position: absolute;
left: 0px;
top: 0px;
- width: $MINIMIZED_ICON_SIZE;
+ width: 8px;
height: $MINIMIZED_ICON_SIZE;
max-height: 20px;
+ > svg {
+ margin:0;
+ }
}
.documentDecorations-background {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 313d8be23..04f02c683 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -3,10 +3,10 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../new_fields/Doc";
-import { Document } from '../../new_fields/documentSchemas';
-import { ScriptField } from '../../new_fields/ScriptField';
-import { Cast, StrCast, NumCast } from "../../new_fields/Types";
+import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../fields/Doc";
+import { Document } from '../../fields/documentSchemas';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, StrCast, NumCast } from "../../fields/Types";
import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
import { DocUtils } from "../documents/Documents";
import { DocumentType } from '../documents/DocumentTypes';
@@ -17,10 +17,11 @@ import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
import { DocumentView } from "./nodes/DocumentView";
import React = require("react");
-import { Id } from '../../new_fields/FieldSymbols';
+import { Id } from '../../fields/FieldSymbols';
import e = require('express');
import { CollectionDockingView } from './collections/CollectionDockingView';
import { SnappingManager } from '../util/SnappingManager';
+import { HtmlField } from '../../fields/HtmlField';
library.add(faCaretUp);
library.add(faObjectGroup);
@@ -266,16 +267,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0;
if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
const project = (p: number[], a: number[], b: number[]) => {
- var atob = [b[0] - a[0], b[1] - a[1]];
- var atop = [p[0] - a[0], p[1] - a[1]];
- var len = atob[0] * atob[0] + atob[1] * atob[1];
- var dot = atop[0] * atob[0] + atop[1] * atob[1];
- var t = dot / len;
+ const atob = [b[0] - a[0], b[1] - a[1]];
+ const atop = [p[0] - a[0], p[1] - a[1]];
+ const len = atob[0] * atob[0] + atob[1] * atob[1];
+ let dot = atop[0] * atob[0] + atop[1] * atob[1];
+ const t = dot / len;
dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
return [a[0] + atob[0] * t, a[1] + atob[1] * t];
- }
+ };
const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1])
+ const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]);
thisPt = DragManager.snapDragAspect(drag, fixedAspect);
} else {
thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY);
@@ -289,7 +290,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let dX = 0, dY = 0, dW = 0, dH = 0;
const unfreeze = () =>
SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) =>
- (element.rootDoc.type === DocumentType.RTF && element.layoutDoc._nativeHeight) && element.toggleNativeDimensions()));
+ ((element.rootDoc.type === DocumentType.RTF ||
+ element.rootDoc.type === DocumentType.COMPARISON ||
+ (element.rootDoc.type === DocumentType.WEB && Doc.LayoutField(element.rootDoc) instanceof HtmlField))
+ && element.layoutDoc._nativeHeight) && element.toggleNativeDimensions()));
switch (this._resizeHdlId) {
case "": break;
case "documentDecorations-topLeftResizer":
@@ -476,12 +480,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<FontAwesomeIcon size="lg" color={SelectionManager.SelectedDocuments()[0].props.Document.title === SelectionManager.SelectedDocuments()[0].props.Document[Id] ? "green" : undefined} icon="sticky-note"></FontAwesomeIcon>
</div>}
</> :
- <div className="documentDecorations-title" onPointerDown={this.onTitleDown} >
- {minimal ? (null) : <div className="documentDecorations-contextMenu" title="Show context menu" onPointerDown={this.onSettingsDown}>
+ <>
+ {minimal ? (null) : <div className="documentDecorations-contextMenu" key="menu" title="Show context menu" onPointerDown={this.onSettingsDown}>
<FontAwesomeIcon size="lg" icon="cog" />
</div>}
- <span style={{ width: "calc(100% - 25px)", display: "inline-block" }}>{`${this.selectionTitle}`}</span>
- </div>;
+ <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
+ <span style={{ width: "calc(100% - 25px)", display: "inline-block" }}>{`${this.selectionTitle}`}</span>
+ </div>
+ </>;
bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index c51173ad3..e0e205df9 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -2,8 +2,8 @@ import React = require('react');
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as Autosuggest from 'react-autosuggest';
-import { ObjectField } from '../../new_fields/ObjectField';
-import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField';
+import { ObjectField } from '../../fields/ObjectField';
+import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
import "./EditableView.scss";
export interface EditableProps {
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 4f8f9ed69..4352ac52c 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -6,14 +6,14 @@ import { computed, observable, action, runInAction, IReactionDisposer, reaction,
import { GestureUtils } from "../../pen-gestures/GestureUtils";
import { InteractionUtils } from "../util/InteractionUtils";
import { InkingControl } from "./InkingControl";
-import { InkTool, InkData } from "../../new_fields/InkField";
-import { Doc } from "../../new_fields/Doc";
+import { InkTool, InkData } from "../../fields/InkField";
+import { Doc } from "../../fields/Doc";
import { LinkManager } from "../util/LinkManager";
import { DocUtils, Docs } from "../documents/Documents";
import { undoBatch } from "../util/UndoManager";
import { Scripting } from "../util/Scripting";
-import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { FieldValue, Cast, NumCast, BoolCast } from "../../fields/Types";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
import HorizontalPalette from "./Palette";
import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, numberRange, returnZero } from "../../Utils";
import { DocumentView } from "./nodes/DocumentView";
@@ -22,9 +22,9 @@ import { DocumentContentsView } from "./nodes/DocumentContentsView";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { DocServer } from "../DocServer";
import htmlToImage from "html-to-image";
-import { ScriptField } from "../../new_fields/ScriptField";
-import { listSpec } from "../../new_fields/Schema";
-import { List } from "../../new_fields/List";
+import { ScriptField } from "../../fields/ScriptField";
+import { listSpec } from "../../fields/Schema";
+import { List } from "../../fields/List";
import { CollectionViewType } from "./collections/CollectionView";
import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
import MobileInterface from "../../mobile/MobileInterface";
@@ -38,10 +38,8 @@ import { SelectionManager } from "../util/SelectionManager";
export default class GestureOverlay extends Touchable {
static Instance: GestureOverlay;
- @observable public Color: string = "rgb(0, 0, 0)";
- @observable public Width: number = 2;
@observable public SavedColor?: string;
- @observable public SavedWidth?: number;
+ @observable public SavedWidth?: string;
@observable public Tool: ToolglassTools = ToolglassTools.None;
@observable private _thumbX?: number;
@@ -711,12 +709,12 @@ export default class GestureOverlay extends Touchable {
this._palette,
[this._strokes.map(l => {
const b = this.getBounds(l);
- return <svg key={b.left} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000 }}>
- {InteractionUtils.CreatePolyline(l, b.left, b.top, GestureOverlay.Instance.Color, GestureOverlay.Instance.Width)}
+ return <svg key={b.left} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
+ {InteractionUtils.CreatePolyline(l, b.left, b.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)}
</svg>;
}),
- this._points.length <= 1 ? (null) : <svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000 }}>
- {InteractionUtils.CreatePolyline(this._points, B.left, B.top, GestureOverlay.Instance.Color, GestureOverlay.Instance.Width)}
+ this._points.length <= 1 ? (null) : <svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
+ {InteractionUtils.CreatePolyline(this._points, B.left, B.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)}
</svg>]
];
}
@@ -806,16 +804,16 @@ Scripting.addGlobal(function setToolglass(tool: any) {
});
Scripting.addGlobal(function setPen(width: any, color: any) {
runInAction(() => {
- GestureOverlay.Instance.SavedColor = GestureOverlay.Instance.Color;
- GestureOverlay.Instance.Color = color;
- GestureOverlay.Instance.SavedWidth = GestureOverlay.Instance.Width;
- GestureOverlay.Instance.Width = width;
+ GestureOverlay.Instance.SavedColor = InkingControl.Instance.selectedColor;
+ InkingControl.Instance.updateSelectedColor(color);
+ GestureOverlay.Instance.SavedWidth = InkingControl.Instance.selectedWidth;
+ InkingControl.Instance.switchWidth(width);
});
});
Scripting.addGlobal(function resetPen() {
runInAction(() => {
- GestureOverlay.Instance.Color = GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)";
- GestureOverlay.Instance.Width = GestureOverlay.Instance.SavedWidth ?? 2;
+ InkingControl.Instance.updateSelectedColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
+ InkingControl.Instance.switchWidth(GestureOverlay.Instance.SavedWidth ?? "2");
});
});
Scripting.addGlobal(function createText(text: any, x: any, y: any) {
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index b52a0063b..255142771 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -4,23 +4,23 @@ import { CollectionDockingView } from "./collections/CollectionDockingView";
import { MainView } from "./MainView";
import { DragManager } from "../util/DragManager";
import { action, runInAction } from "mobx";
-import { Doc, DocListCast } from "../../new_fields/Doc";
+import { Doc, DocListCast } from "../../fields/Doc";
import { DictationManager } from "../util/DictationManager";
import SharingManager from "../util/SharingManager";
-import { Cast, PromiseValue, NumCast } from "../../new_fields/Types";
-import { ScriptField } from "../../new_fields/ScriptField";
+import { Cast, PromiseValue, NumCast } from "../../fields/Types";
+import { ScriptField } from "../../fields/ScriptField";
import { InkingControl } from "./InkingControl";
-import { InkTool } from "../../new_fields/InkField";
+import { InkTool } from "../../fields/InkField";
import { DocumentView } from "./nodes/DocumentView";
import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView";
import { MarqueeView } from "./collections/collectionFreeForm/MarqueeView";
-import { Id } from "../../new_fields/FieldSymbols";
+import { Id } from "../../fields/FieldSymbols";
import { DocumentDecorations } from "./DocumentDecorations";
import { DocumentType } from "../documents/DocumentTypes";
import { DocServer } from "../DocServer";
-import { List } from "../../new_fields/List";
-import { DateField } from "../../new_fields/DateField";
+import { List } from "../../fields/List";
+import { DateField } from "../../fields/DateField";
const modifiers = ["control", "meta", "shift", "alt"];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index 81d99e009..41ee36d05 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -1,9 +1,9 @@
import { action, computed, observable } from "mobx";
import { ColorState } from 'react-color';
-import { Doc } from "../../new_fields/Doc";
-import { InkTool } from "../../new_fields/InkField";
-import { FieldValue, NumCast, StrCast } from "../../new_fields/Types";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { Doc } from "../../fields/Doc";
+import { InkTool } from "../../fields/InkField";
+import { FieldValue, NumCast, StrCast } from "../../fields/Types";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { Scripting } from "../util/Scripting";
import { SelectionManager } from "../util/SelectionManager";
import { undoBatch } from "../util/UndoManager";
@@ -13,8 +13,8 @@ import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
export class InkingControl {
@observable static Instance: InkingControl;
@computed private get _selectedTool(): InkTool { return FieldValue(NumCast(Doc.UserDoc().inkTool)) ?? InkTool.None; }
- @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(Doc.UserDoc().inkColor)) ?? "rgb(244, 67, 54)"; }
- @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "5"; }
+ @computed private get _selectedColor(): string { return CurrentUserUtils.ActivePen ? FieldValue(StrCast(CurrentUserUtils.ActivePen.backgroundColor)) ?? "rgb(0, 0, 0)" : "rgb(0, 0, 0)"; }
+ @computed private get _selectedWidth(): string { return FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "2"; }
@observable public _open: boolean = false;
constructor() {
@@ -35,7 +35,8 @@ export class InkingControl {
@undoBatch
switchColor = action((color: ColorState): void => {
Doc.UserDoc().backgroundColor = color.hex.startsWith("#") ?
- color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex;
+ color.hex + (color.rgb.a ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex;
+ CurrentUserUtils.ActivePen && (CurrentUserUtils.ActivePen.backgroundColor = color.hex);
if (InkingControl.Instance.selectedTool === InkTool.None) {
const selected = SelectionManager.SelectedDocuments();
@@ -51,14 +52,14 @@ export class InkingControl {
}
}
});
- } else {
- CurrentUserUtils.ActivePen && (CurrentUserUtils.ActivePen.backgroundColor = this._selectedColor);
}
});
@action
switchWidth = (width: string): void => {
// this._selectedWidth = width;
- Doc.UserDoc().inkWidth = width;
+ if (!isNaN(parseInt(width))) {
+ Doc.UserDoc().inkWidth = width;
+ }
}
@computed
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 7a318d5c2..8938e8b6c 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,14 +1,14 @@
import { observer } from "mobx-react";
-import { documentSchema } from "../../new_fields/documentSchemas";
-import { InkData, InkField, InkTool } from "../../new_fields/InkField";
-import { makeInterface } from "../../new_fields/Schema";
-import { Cast, StrCast, NumCast } from "../../new_fields/Types";
+import { documentSchema } from "../../fields/documentSchemas";
+import { InkData, InkField, InkTool } from "../../fields/InkField";
+import { makeInterface } from "../../fields/Schema";
+import { Cast, StrCast, NumCast } from "../../fields/Types";
import { ViewBoxBaseComponent } from "./DocComponent";
import { InkingControl } from "./InkingControl";
import "./InkingStroke.scss";
import { FieldView, FieldViewProps } from "./nodes/FieldView";
import React = require("react");
-import { TraceMobx } from "../../new_fields/util";
+import { TraceMobx } from "../../fields/util";
import { InteractionUtils } from "../util/InteractionUtils";
import { ContextMenu } from "./ContextMenu";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
@@ -40,7 +40,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const bottom = Math.max(...ys);
const points = InteractionUtils.CreatePolyline(data, left, top,
StrCast(this.layoutDoc.color, InkingControl.Instance.selectedColor),
- NumCast(this.layoutDoc.strokeWidth, parseInt(InkingControl.Instance.selectedWidth)));
+ StrCast(this.layoutDoc.strokeWidth, InkingControl.Instance.selectedWidth));
const width = right - left;
const height = bottom - top;
const scaleX = this.props.PanelWidth() / width;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index b21eb9c8f..6878658a8 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,16 +1,20 @@
import { MainView } from "./MainView";
import { Docs } from "../documents/Documents";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
import * as ReactDOM from 'react-dom';
import * as React from 'react';
import { DocServer } from "../DocServer";
import { AssignAllExtensions } from "../../extensions/General/Extensions";
+import { Networking } from "../Network";
AssignAllExtensions();
+export let resolvedPorts: { server: number, socket: number };
+
(async () => {
const info = await CurrentUserUtils.loadCurrentUser();
- DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email);
+ resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts"));
+ DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, info.email);
await Docs.Prototypes.initialize();
if (info.id !== "__guest__") {
// a guest will not have an id registered
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9bfef06b4..a1d1b0ece 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,11 +1,11 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import {
- faTrashAlt, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+ faTasks, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
- faThumbtack, faTree, faTv, faUndoAlt, faVideo
+ faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
@@ -13,13 +13,13 @@ import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
import Measure from 'react-measure';
-import { Doc, DocListCast, Field, Opt } from '../../new_fields/Doc';
-import { Id } from '../../new_fields/FieldSymbols';
-import { List } from '../../new_fields/List';
-import { listSpec } from '../../new_fields/Schema';
-import { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types';
-import { TraceMobx } from '../../new_fields/util';
-import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
+import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
+import { listSpec } from '../../fields/Schema';
+import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
+import { TraceMobx } from '../../fields/util';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, returnTrue, Utils } from '../../Utils';
import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
@@ -48,10 +48,11 @@ import { RadialMenu } from './nodes/RadialMenu';
import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
-import { ScriptField } from '../../new_fields/ScriptField';
+import { ScriptField } from '../../fields/ScriptField';
import { TimelineMenu } from './animationtimeline/TimelineMenu';
-import { DragManager } from '../util/DragManager';
import { SnappingManager } from '../util/SnappingManager';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { DocumentManager } from '../util/DocumentManager';
@observer
export class MainView extends React.Component {
@@ -66,7 +67,7 @@ export class MainView extends React.Component {
@observable private _panelHeight: number = 0;
@observable private _flyoutTranslate: boolean = true;
@observable public flyoutWidth: number = 250;
- private get darkScheme() { return BoolCast(Cast(this.userDoc.activeWorkspace, Doc, null)?.darkScheme); }
+ private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); }
@computed private get userDoc() { return Doc.UserDoc(); }
@computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
@@ -84,6 +85,14 @@ export class MainView extends React.Component {
window.removeEventListener("keydown", KeyManager.Instance.handle);
window.addEventListener("keydown", KeyManager.Instance.handle);
window.addEventListener("paste", KeyManager.Instance.paste as any);
+ document.addEventListener("dash", (e: any) => { // event used by chrome plugin to tell Dash which document to focus on
+ const id = FormattedTextBox.GetDocFromUrl(e.detail);
+ DocServer.GetRefField(id).then(doc => {
+ if (doc instanceof Doc) {
+ DocumentManager.Instance.jumpToDocument(doc, false, undefined);
+ }
+ });
+ });
}
componentWillUnMount() {
@@ -114,77 +123,12 @@ export class MainView extends React.Component {
}
}
- library.add(faTrashAlt);
- library.add(faAngleRight);
- library.add(faBell);
- library.add(faTrash);
- library.add(faCamera);
- library.add(faExpand);
- library.add(faCaretDown);
- library.add(faCaretRight);
- library.add(faCaretSquareDown);
- library.add(faCaretSquareRight);
- library.add(faArrowsAltH);
- library.add(faPlus, faMinus);
- library.add(faTerminal);
- library.add(faToggleOn);
- library.add(faLocationArrow);
- library.add(faSearch);
- library.add(fileSolid);
- library.add(faFileDownload);
- library.add(faStop);
- library.add(faCalculator);
- library.add(faWindowMaximize);
- library.add(faFileAlt);
- library.add(faAddressCard);
- library.add(faQuestionCircle);
- library.add(faStickyNote);
- library.add(faFont);
- library.add(faExclamation);
- library.add(faPortrait);
- library.add(faCat);
- library.add(faFilePdf);
- library.add(faObjectGroup);
- library.add(faTv);
- library.add(faGlobeAsia);
- library.add(faUndoAlt);
- library.add(faRedoAlt);
- library.add(faMousePointer);
- library.add(faPen);
- library.add(faHighlighter);
- library.add(faEraser);
- library.add(faFileAudio);
- library.add(faPenNib);
- library.add(faMicrophone);
- library.add(faFilm);
- library.add(faMusic);
- library.add(faTree);
- library.add(faPlay);
- library.add(faCompressArrowsAlt);
- library.add(faPause);
- library.add(faClone);
- library.add(faCut);
- library.add(faCommentAlt);
- library.add(faThumbtack);
- library.add(faLongArrowAltRight);
- library.add(faCheck);
- library.add(faCaretUp);
- library.add(faFilter);
- library.add(faBullseye);
- library.add(faArrowLeft);
- library.add(faArrowRight);
- library.add(faArrowDown);
- library.add(faArrowUp);
- library.add(faCloudUploadAlt);
- library.add(faBolt);
- library.add(faVideo);
- library.add(faChevronRight);
- library.add(faEllipsisV);
- library.add(faMusic);
- library.add(faPhone);
- library.add(faClipboard);
- library.add(faStamp);
- library.add(faExternalLinkAlt);
+ library.add(faTasks, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+ faTerminal, faToggleOn, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, fileSolid,
+ faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
+ faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
+ faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell,
+ faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye);
this.initEventListeners();
this.initAuthenticationRouters();
}
@@ -204,8 +148,8 @@ export class MainView extends React.Component {
globalPointerUp = () => this.isPointerDown = false;
initEventListeners = () => {
- window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
- window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
+ window.addEventListener("drop", (e) => { e.preventDefault(); }, false); // drop event handler
+ window.addEventListener("dragover", (e) => { e.preventDefault(); }, false); // drag event handler
// click interactions for the context menu
document.addEventListener("pointerdown", this.globalPointerDown);
document.addEventListener("pointerup", this.globalPointerUp);
@@ -252,15 +196,17 @@ export class MainView extends React.Component {
_LODdisable: true
};
const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
- const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
+ const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
- mainDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!]);
- mainDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors"]);
+ const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+ const cloneWorkspace = ScriptField.MakeScript(`cloneWorkspace()`);
+ workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneWorkspace!]);
+ workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Workspace Layout"]);
- Doc.AddDocToList(workspaces, "data", mainDoc);
+ Doc.AddDocToList(workspaces, "data", workspaceDoc);
// bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => this.openWorkspace(mainDoc), 0);
+ setTimeout(() => this.openWorkspace(workspaceDoc), 0);
}
@action
@@ -307,7 +253,6 @@ export class MainView extends React.Component {
onDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
- console.log("Drop");
}
@action
@@ -374,7 +319,9 @@ export class MainView extends React.Component {
const width = this.flyoutWidth;
return <Measure offset onResize={this.onResize}>
{({ measureRef }) =>
- <div ref={measureRef} className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
+ <div ref={measureRef} className="mainContent-div" onDragEnter={e => {
+ console.log("ENTERING");
+ }} onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
{!mainContainer ? (null) : this.mainDocView}
</div>
}
@@ -597,7 +544,7 @@ export class MainView extends React.Component {
}
@computed get snapLines() {
- return <div className="mainView-snapLines">
+ return !Doc.UserDoc().showSnapLines ? (null) : <div className="mainView-snapLines">
<svg style={{ width: "100%", height: "100%" }}>
{SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
{SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
@@ -628,3 +575,11 @@ export class MainView extends React.Component {
}
}
Scripting.addGlobal(function freezeSidebar() { MainView.expandFlyout(); });
+Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
+Scripting.addGlobal(function cloneWorkspace() {
+ const copiedWorkspace = Doc.MakeCopy(Cast(Doc.UserDoc().activeWorkspace, Doc, null), true);
+ const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc, null);
+ Doc.AddDocToList(workspaces, "data", copiedWorkspace);
+ // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
+ setTimeout(() => MainView.Instance.openWorkspace(copiedWorkspace), 0);
+});
diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx
index 82e07c449..05f890485 100644
--- a/src/client/views/MainViewNotifs.tsx
+++ b/src/client/views/MainViewNotifs.tsx
@@ -2,7 +2,7 @@ import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { emptyFunction } from '../../Utils';
import { SetupDrag } from '../util/DragManager';
import "./MainViewNotifs.scss";
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 8bc80ed06..e100d3f52 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -3,7 +3,7 @@ import "./MetadataEntryMenu.scss";
import { observer } from 'mobx-react';
import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
import { KeyValueBox } from './nodes/KeyValueBox';
-import { Doc, Field, DocListCastAsync } from '../../new_fields/Doc';
+import { Doc, Field, DocListCastAsync } from '../../fields/Doc';
import * as Autosuggest from 'react-autosuggest';
import { undoBatch } from '../util/UndoManager';
import { emptyFunction, emptyPath } from '../../Utils';
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index afb6bfb7d..cfa869fb2 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,16 +1,19 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
-import { Doc, DocListCast, Opt } from "../../new_fields/Doc";
-import { Id } from "../../new_fields/FieldSymbols";
-import { NumCast } from "../../new_fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils } from "../../Utils";
+import { Doc, DocListCast, Opt } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { NumCast, Cast } from "../../fields/Types";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents } from "../../Utils";
import { Transform } from "../util/Transform";
import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
import { DocumentView } from "./nodes/DocumentView";
import './OverlayView.scss';
import { Scripting } from "../util/Scripting";
import { ScriptingRepl } from './ScriptingRepl';
+import { DragManager } from "../util/DragManager";
+import { listSpec } from "../../fields/Schema";
+import { List } from "../../fields/List";
export type OverlayDisposer = () => void;
@@ -139,46 +142,51 @@ export class OverlayView extends React.Component {
return remove;
}
+
@computed get overlayDocs() {
const userDocOverlays = Doc.UserDoc().myOverlayDocuments;
if (!userDocOverlays) {
- return (null);
+ return null;
}
return userDocOverlays instanceof Doc && DocListCast(userDocOverlays.data).map(d => {
setTimeout(() => d.inOverlay = true, 0);
let offsetx = 0, offsety = 0;
- const onPointerMove = action((e: PointerEvent) => {
+ const dref = React.createRef<HTMLDivElement>();
+ const onPointerMove = action((e: PointerEvent, down: number[]) => {
if (e.buttons === 1) {
d.x = e.clientX + offsetx;
d.y = e.clientY + offsety;
- e.stopPropagation();
- e.preventDefault();
}
- });
- const onPointerUp = action((e: PointerEvent) => {
- document.removeEventListener("pointermove", onPointerMove);
- document.removeEventListener("pointerup", onPointerUp);
- e.stopPropagation();
- e.preventDefault();
+ if (e.metaKey) {
+ const dragData = new DragManager.DocumentDragData([d]);
+ d.removeDropProperties = new List<string>(["inOverlay"]);
+ dragData.offset = [-offsetx, -offsety];
+ dragData.dropAction = "move";
+ dragData.removeDocument = (doc: Doc | Doc[]) => {
+ const docs = (doc instanceof Doc) ? [doc] : doc;
+ docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocuments, Doc, null), "data", d));
+ return true;
+ };
+ dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
+ return dragData.removeDocument!(doc) ? addDocument(doc) : false;
+ };
+ DragManager.StartDocumentDrag([dref.current!], dragData, down[0], down[1]);
+ return true;
+ }
+ return false;
});
const onPointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, onPointerMove, emptyFunction, emptyFunction);
offsetx = NumCast(d.x) - e.clientX;
offsety = NumCast(d.y) - e.clientY;
- e.stopPropagation();
- e.preventDefault();
- document.addEventListener("pointermove", onPointerMove);
- document.addEventListener("pointerup", onPointerUp);
};
- return <div className="overlayView-doc" key={d[Id]} onPointerDown={onPointerDown} style={{ transform: `translate(${d.x}px, ${d.y}px)` }}>
+ return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
<DocumentView
Document={d}
LibraryPath={emptyPath}
ChromeHeight={returnZero}
rootSelected={returnTrue}
- // isSelected={returnFalse}
- // select={emptyFunction}
- // layoutKey={"layout"}
bringToFront={emptyFunction}
addDocument={undefined}
removeDocument={undefined}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 63744cb50..108eb83d6 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -1,8 +1,8 @@
import { IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
-import { Doc } from "../../new_fields/Doc";
-import { NumCast } from "../../new_fields/Types";
+import { Doc } from "../../fields/Doc";
+import { NumCast } from "../../fields/Types";
import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue } from "../../Utils";
import { Transform } from "../util/Transform";
import { DocumentView } from "./nodes/DocumentView";
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index f7a7944c9..dd65681d4 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -4,11 +4,11 @@ import "normalize.css";
import * as React from 'react';
import "./PreviewCursor.scss";
import { Docs } from '../documents/Documents';
-import { Doc } from '../../new_fields/Doc';
+import { Doc } from '../../fields/Doc';
import { Transform } from "../util/Transform";
import { DocServer } from '../DocServer';
import { undoBatch } from '../util/UndoManager';
-import { NumCast } from '../../new_fields/Types';
+import { NumCast } from '../../fields/Types';
@observer
export class PreviewCursor extends React.Component<{}> {
@@ -59,12 +59,15 @@ export class PreviewCursor extends React.Component<{}> {
const pty = Number(strs[1].substring(0, strs[1].length - 1));
let count = 1;
const list: Doc[] = [];
+
+ let first: Doc | undefined;
docids.map((did, i) => i && DocServer.GetRefField(did).then(doc => {
count++;
if (doc instanceof Doc) {
+ i === 1 && (first = doc);
const alias = Doc.MakeClone(doc);
- const deltaX = NumCast(doc.x) - ptx;
- const deltaY = NumCast(doc.y) - pty;
+ const deltaX = NumCast(doc.x) - NumCast(first!.x) - ptx;
+ const deltaY = NumCast(doc.y) - NumCast(first!.y) - pty;
alias.x = newPoint[0] + deltaX;
alias.y = newPoint[1] + deltaY;
list.push(alias);
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index e66fd3eb4..8ca81c070 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -3,17 +3,17 @@ import React = require("react");
import { observable, action, computed, runInAction } from "mobx";
import Measure from "react-measure";
import "./RecommendationsBox.scss";
-import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc";
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../fields/Doc";
import { DocumentIcon } from "./nodes/DocumentIcon";
-import { StrCast, NumCast } from "../../new_fields/Types";
+import { StrCast, NumCast } from "../../fields/Types";
import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath, returnZero } from "../../Utils";
import { Transform } from "../util/Transform";
-import { ObjectField } from "../../new_fields/ObjectField";
+import { ObjectField } from "../../fields/ObjectField";
import { DocumentView } from "./nodes/DocumentView";
import { DocumentType } from '../documents/DocumentTypes';
import { ClientRecommender } from "../ClientRecommender";
import { DocServer } from "../DocServer";
-import { Id } from "../../new_fields/FieldSymbols";
+import { Id } from "../../fields/FieldSymbols";
import { FieldView, FieldViewProps } from "./nodes/FieldView";
import { DocumentManager } from "../util/DocumentManager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 66d3b937e..888f84dfa 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -5,11 +5,11 @@ import { observable, action } from "mobx";
import "./ScriptBox.scss";
import { OverlayView } from "./OverlayView";
import { DocumentIconContainer } from "./nodes/DocumentIcon";
-import { Opt, Doc } from "../../new_fields/Doc";
+import { Opt, Doc } from "../../fields/Doc";
import { emptyFunction } from "../../Utils";
-import { ScriptCast } from "../../new_fields/Types";
+import { ScriptCast } from "../../fields/Types";
import { CompileScript } from "../util/Scripting";
-import { ScriptField } from "../../new_fields/ScriptField";
+import { ScriptField } from "../../fields/ScriptField";
import { DragManager } from "../util/DragManager";
import { EditableView } from "./EditableView";
import { getEffectiveTypeRoots } from "typescript";
diff --git a/src/client/views/SearchDocBox.tsx b/src/client/views/SearchDocBox.tsx
index 7bd689b19..e038d8213 100644
--- a/src/client/views/SearchDocBox.tsx
+++ b/src/client/views/SearchDocBox.tsx
@@ -3,9 +3,9 @@ import { faBullseye, faLink } from "@fortawesome/free-solid-svg-icons";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
//import "./SearchBoxDoc.scss";
-import { Doc, DocListCast } from "../../new_fields/Doc";
-import { Id } from "../../new_fields/FieldSymbols";
-import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
import { returnFalse, returnZero } from "../../Utils";
import { Docs } from "../documents/Documents";
import { SearchUtil } from "../util/SearchUtil";
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 43debfe99..77e6ebf44 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -6,15 +6,16 @@ import './TemplateMenu.scss';
import { DocumentView } from "./nodes/DocumentView";
import { Template } from "./Templates";
import React = require("react");
-import { Doc, DocListCast } from "../../new_fields/Doc";
+import { Doc, DocListCast } from "../../fields/Doc";
import { Docs, } from "../documents/Documents";
-import { StrCast, Cast } from "../../new_fields/Types";
+import { StrCast, Cast } from "../../fields/Types";
import { CollectionTreeView } from "./collections/CollectionTreeView";
import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero } from "../../Utils";
import { Transform } from "../util/Transform";
-import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
+import { ScriptField, ComputedField } from "../../fields/ScriptField";
import { Scripting } from "../util/Scripting";
-import { List } from "../../new_fields/List";
+import { List } from "../../fields/List";
+import { TraceMobx } from "../../fields/util";
@observer
class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
@@ -110,7 +111,12 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
return ScriptField.MakeScript("docs.map(d => switchView(d, this))", { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name, firstDoc: Doc.name },
{ docs: new List<Doc>(this.props.docViews.map(dv => dv.props.Document)) });
}
+ templateIsUsed = (selDoc: Doc, templateDoc: Doc) => {
+ const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title);
+ return StrCast(selDoc.layoutKey) === "layout_" + template ? 'check' : 'unchecked';
+ }
render() {
+ TraceMobx();
const firstDoc = this.props.docViews[0].props.Document;
const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", "");
const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
@@ -123,7 +129,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
templateMenu.push(<OtherToggle key={"float"} name={"Float"} checked={firstDoc.z ? true : false} toggle={this.toggleFloat} />);
templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
- addedTypes.concat(noteTypes).map(template => template.treeViewChecked = ComputedField.MakeFunction(`templateIsUsed(self,firstDoc)`, {}, { firstDoc }));
+ addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push(
<OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
return <ul className="template-list" style={{ display: "block" }}>
@@ -172,11 +178,3 @@ Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) {
const templateTitle = StrCast(template?.title);
return templateTitle && Doc.makeCustomViewClicked(doc, Docs.Create.FreeformDocument, templateTitle, template);
});
-
-Scripting.addGlobal(function templateIsUsed(templateDoc: Doc, selDoc: Doc) {
- if (selDoc) {
- const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title);
- return StrCast(selDoc.layoutKey) === "layout_" + template ? 'check' : 'unchecked';
- }
- return false;
-}); \ No newline at end of file
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index bbd7b2676..b562bd957 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -4,19 +4,17 @@ import "./Timeline.scss";
import "../globalCssVariables.scss";
import { observer } from "mobx-react";
import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { Cast, NumCast } from "../../../new_fields/Types";
-import { List } from "../../../new_fields/List";
-import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
+import { Cast, NumCast } from "../../../fields/Types";
+import { List } from "../../../fields/List";
+import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../fields/Schema";
import { Transform } from "../../util/Transform";
import { TimelineMenu } from "./TimelineMenu";
import { Docs } from "../../documents/Documents";
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
import { emptyPath } from "../../../Utils";
-
/**
* Useful static functions that you can use. Mostly for logic, but you can also add UI logic here also
*/
@@ -100,16 +98,11 @@ export namespace KeyframeFunc {
export const convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time", tickSpacing: number, tickIncrement: number) => {
const time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement;
switch (unit) {
- case "mili":
- return time;
- case "sec":
- return dir === "pixel" ? time / 1000 : time * 1000;
- case "min":
- return dir === "pixel" ? time / 60000 : time * 60000;
- case "hr":
- return dir === "pixel" ? time / 3600000 : time * 3600000;
- default:
- return time;
+ case "mili": return time;
+ case "sec": return dir === "pixel" ? time / 1000 : time * 1000;
+ case "min": return dir === "pixel" ? time / 60000 : time * 60000;
+ case "hr": return dir === "pixel" ? time / 3600000 : time * 3600000;
+ default: return time;
}
};
}
@@ -187,10 +180,10 @@ export class Keyframe extends React.Component<IProps> {
const fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade);
const fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade);
const finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.end);
- (fadeIn.key as Doc).opacity = 1;
- (fadeOut.key as Doc).opacity = 1;
- (start.key as Doc).opacity = 0.1;
- (finish.key as Doc).opacity = 0.1;
+ (fadeIn as Doc).opacity = 1;
+ (fadeOut as Doc).opacity = 1;
+ (start as Doc).opacity = 0.1;
+ (finish as Doc).opacity = 0.1;
this.forceUpdate(); //not needed, if setTimeout is gone...
}, 1000);
}
@@ -333,31 +326,28 @@ export class Keyframe extends React.Component<IProps> {
*/
@action
makeKeyframeMenu = (kf: Doc, e: MouseEvent) => {
- TimelineMenu.Instance.addItem("button", "Show Data", () => {
- runInAction(() => {
+ TimelineMenu.Instance.addItem("button", "Toggle Fade Only", () => {
+ kf.type = kf.type === KeyframeFunc.KeyframeType.fade ? KeyframeFunc.KeyframeType.default : KeyframeFunc.KeyframeType.fade;
+ }),
+ TimelineMenu.Instance.addItem("button", "Show Data", action(() => {
const kvp = Docs.Create.KVPDocument(kf, { _width: 300, _height: 300 });
CollectionDockingView.AddRightSplit(kvp, emptyPath);
- });
- }),
- TimelineMenu.Instance.addItem("button", "Delete", () => {
- runInAction(() => {
- (this.regiondata.keyframes as List<Doc>).splice(this.keyframes.indexOf(kf), 1);
- this.forceUpdate();
- });
- }),
- TimelineMenu.Instance.addItem("input", "Move", (val) => {
- runInAction(() => {
- let cannotMove: boolean = false;
- const kfIndex: number = this.keyframes.indexOf(kf);
- if (val < 0 || (val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time))) {
- cannotMove = true;
- }
- if (!cannotMove) {
- this.keyframes[kfIndex].time = parseInt(val, 10);
- this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn;
- }
- });
- });
+ })),
+ TimelineMenu.Instance.addItem("button", "Delete", action(() => {
+ (this.regiondata.keyframes as List<Doc>).splice(this.keyframes.indexOf(kf), 1);
+ this.forceUpdate();
+ })),
+ TimelineMenu.Instance.addItem("input", "Move", action((val) => {
+ let cannotMove: boolean = false;
+ const kfIndex: number = this.keyframes.indexOf(kf);
+ if (val < 0 || (val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time))) {
+ cannotMove = true;
+ }
+ if (!cannotMove) {
+ this.keyframes[kfIndex].time = parseInt(val, 10);
+ this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn;
+ }
+ }));
TimelineMenu.Instance.addMenu("Keyframe");
TimelineMenu.Instance.openMenu(e.clientX, e.clientY);
}
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 466cbb867..30692944d 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -1,12 +1,12 @@
import * as React from "react";
import "./Timeline.scss";
-import { listSpec } from "../../../new_fields/Schema";
+import { listSpec } from "../../../fields/Schema";
import { observer } from "mobx-react";
import { Track } from "./Track";
import { observable, action, computed, runInAction, IReactionDisposer, reaction, trace } from "mobx";
-import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { List } from "../../../new_fields/List";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types";
+import { List } from "../../../fields/List";
+import { Doc, DocListCast } from "../../../fields/Doc";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlayCircle, faBackward, faForward, faGripLines, faPauseCircle, faEyeSlash, faEye, faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons";
import { ContextMenu } from "../ContextMenu";
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index 461db4858..fc96c320a 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -1,12 +1,12 @@
import { action, computed, intercept, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
-import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../new_fields/Doc";
-import { Copy } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { ObjectField } from "../../../new_fields/ObjectField";
-import { listSpec } from "../../../new_fields/Schema";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc";
+import { Copy } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { ObjectField } from "../../../fields/ObjectField";
+import { listSpec } from "../../../fields/Schema";
+import { Cast, NumCast } from "../../../fields/Types";
import { Transform } from "../../util/Transform";
import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe";
import "./Track.scss";
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index a04136e51..f65a89422 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -2,18 +2,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { observable, computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { documentSchema, collectionSchema } from '../../../new_fields/documentSchemas';
-import { makeInterface } from '../../../new_fields/Schema';
-import { NumCast, StrCast, ScriptCast, Cast } from '../../../new_fields/Types';
+import { documentSchema, collectionSchema } from '../../../fields/documentSchemas';
+import { makeInterface } from '../../../fields/Schema';
+import { NumCast, StrCast, ScriptCast, Cast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
import "./CollectionCarouselView.scss";
import { CollectionSubView } from './CollectionSubView';
-import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc } from '../../../fields/Doc';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { ContextMenu } from '../ContextMenu';
-import { ObjectField } from '../../../new_fields/ObjectField';
+import { ObjectField } from '../../../fields/ObjectField';
import { returnFalse } from '../../../Utils';
type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
@@ -76,10 +75,10 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
@computed get buttons() {
return <>
<div key="back" className="carouselView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.goback}>
- <FontAwesomeIcon icon={faCaretLeft} size={"2x"} />
+ <FontAwesomeIcon icon={"caret-left"} size={"2x"} />
</div>
<div key="fwd" className="carouselView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.advance}>
- <FontAwesomeIcon icon={faCaretRight} size={"2x"} />
+ <FontAwesomeIcon icon={"caret-right"} size={"2x"} />
</div>
</>;
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 581625222..6f5a3dfe4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,5 +1,3 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faFile } from '@fortawesome/free-solid-svg-icons';
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
@@ -7,13 +5,13 @@ import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import Measure from "react-measure";
import * as GoldenLayout from "../../../client/goldenLayout";
-import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast, Field, Opt, DataSym } from "../../../new_fields/Doc";
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { FieldId } from "../../../new_fields/RefField";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from '../../../new_fields/util';
+import { DateField } from '../../../fields/DateField';
+import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc";
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { FieldId } from "../../../fields/RefField";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from '../../../fields/util';
import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
@@ -31,7 +29,6 @@ import { DockingViewButtonSelector } from './ParentDocumentSelector';
import React = require("react");
import { CollectionViewType } from './CollectionView';
import { SnappingManager } from '../../util/SnappingManager';
-library.add(faFile);
const _global = (window /* browser */ || global /* node */) as any;
@observer
@@ -196,15 +193,16 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
if (!CollectionDockingView.Instance) return undefined;
const instance = CollectionDockingView.Instance;
const replaceTab = (doc: Doc, child: any): Opt<Doc> => {
- for (let i = 0; i < child.contentItems.length; i++) {
- if (child.contentItems[i].isRow || child.contentItems[i].isColumn || child.contentItems[i].isStack) {
- const val = replaceTab(doc, child.contentItems[i]);
+ for (const contentItem of child.contentItems) {
+ const { config, isStack, isRow, isColumn } = contentItem;
+ if (isRow || isColumn || isStack) {
+ const val = replaceTab(doc, contentItem);
if (val) return val;
- } else if (child.contentItems[i].config.component === "DocumentFrameRenderer" &&
- child.contentItems[i].config.props.documentId === doc[Id]) {
+ } else if (config.component === "DocumentFrameRenderer" &&
+ config.props.documentId === doc[Id]) {
const alias = Doc.MakeAlias(doc);
- child.contentItems[i].config.props.documentId = alias[Id];
- child.contentItems[i].config.title = alias.title;
+ config.props.documentId = alias[Id];
+ config.title = alias.title;
instance.stateChanged();
return alias;
}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 344dca23a..f1002044a 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -1,9 +1,9 @@
import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc';
-import { makeInterface } from '../../../new_fields/Schema';
-import { BoolCast, NumCast, StrCast, Cast, ScriptCast } from '../../../new_fields/Types';
+import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { makeInterface } from '../../../fields/Schema';
+import { BoolCast, NumCast, StrCast, Cast, ScriptCast } from '../../../fields/Types';
import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse, returnZero } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { Transform } from '../../util/Transform';
@@ -11,8 +11,8 @@ import "./CollectionLinearView.scss";
import { CollectionViewType } from './CollectionView';
import { CollectionSubView } from './CollectionSubView';
import { DocumentView } from '../nodes/DocumentView';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { Id } from '../../../new_fields/FieldSymbols';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
type LinearDocument = makeInterface<[typeof documentSchema,]>;
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 971224482..a0b7cd8a8 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,10 +1,10 @@
import { GoogleApiWrapper, Map as GeoMap, IMapProps, Marker } from "google-maps-react";
import { observer } from "mobx-react";
-import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
+import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { Id } from "../../../fields/FieldSymbols";
+import { makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import "./CollectionMapView.scss";
import { CollectionSubView } from "./CollectionSubView";
import React = require("react");
@@ -47,7 +47,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps>
private _cancelAddrReq = new Map<string, boolean>();
private _cancelLocReq = new Map<string, boolean>();
private _initialLookupPending = new Map<string, boolean>();
- private responders: { location: Lambda, address: Lambda }[] = [];
+ private responders: { locationDisposer: Lambda, addressDisposer: Lambda }[] = [];
/**
* Note that all the uses of runInAction below are not included
@@ -176,13 +176,16 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps>
}
@computed get reactiveContents() {
- this.responders.forEach(({ location, address }) => { location(); address(); });
+ this.responders.forEach(({ locationDisposer, addressDisposer }) => {
+ locationDisposer();
+ addressDisposer();
+ });
this.responders = [];
return this.childLayoutPairs.map(({ layout }) => {
const fieldKey = Doc.LayoutFieldKey(layout);
const id = layout[Id];
this.responders.push({
- location: computed(() => ({ lat: layout[`${fieldKey}-lat`], lng: layout[`${fieldKey}-lng`] }))
+ locationDisposer: computed(() => ({ lat: layout[`${fieldKey}-lat`], lng: layout[`${fieldKey}-lng`] }))
.observe(({ oldValue, newValue }) => {
if (this._cancelLocReq.get(id)) {
this._cancelLocReq.set(id, false);
@@ -190,7 +193,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps>
this.respondToLocationChange(layout, fieldKey, newValue, oldValue);
}
}),
- address: computed(() => Cast(layout[`${fieldKey}-address`], "string", null))
+ addressDisposer: computed(() => Cast(layout[`${fieldKey}-address`], "string", null))
.observe(({ oldValue, newValue }) => {
if (this._cancelAddrReq.get(id)) {
this._cancelAddrReq.set(id, false);
@@ -206,7 +209,8 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps>
render() {
const { childLayoutPairs } = this;
const { Document, fieldKey, active, google } = this.props;
- let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false);
+ const mapLoc = this.getLocation(this.rootDoc, `${fieldKey}-mapCenter`, false);
+ let center = mapLoc;
if (center === undefined) {
const childLocations = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false));
center = childLocations.find(location => location) || defaultLocation;
@@ -222,7 +226,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps>
initialCenter={center}
center={center}
onIdle={(_props?: IMapProps, map?: google.maps.Map) => {
- if (this.layoutDoc.lockedTransform) {
+ if (this.layoutDoc._lockedTransform) {
// reset zoom (ideally, we could probably can tell the map to disallow zooming somehow instead)
map?.setZoom(center?.zoom || 10);
map?.setCenter({ lat: center?.lat!, lng: center?.lng! });
@@ -234,7 +238,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps>
}
}}
onDragend={(_props?: IMapProps, map?: google.maps.Map) => {
- if (this.layoutDoc.lockedTransform) {
+ if (this.layoutDoc._lockedTransform) {
// reset the drag (ideally, we could probably can tell the map to disallow dragging somehow instead)
map?.setCenter({ lat: center?.lat!, lng: center?.lng! });
} else {
@@ -246,6 +250,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps>
}}
>
{this.reactiveContents}
+ {mapLoc ? this.renderMarker(this.rootDoc) : undefined}
</GeoMap>
</div>
</div>;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 95c7643c9..cc7a9f5ac 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -1,13 +1,11 @@
import React = require("react");
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { StrCast, NumCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { ScriptField } from "../../../fields/ScriptField";
+import { StrCast, NumCast } from "../../../fields/Types";
import { numberRange, setupMoveUpEvents, emptyFunction } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
@@ -22,8 +20,6 @@ const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-library.add(faPalette);
-
interface CMVFieldRowProps {
rows: () => number;
headings: () => object[];
@@ -244,13 +240,15 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
toggle: this.toggleVisibility,
color: this.color
};
+ const showChrome = (chromeStatus !== 'view-mode' && chromeStatus !== 'disabled');
+ const stackPad = showChrome ? `0px ${this.props.parent.xMargin}px` : `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px 0px ${this.props.parent.xMargin}px `;
return this.collapsed ? (null) :
<div style={{ position: "relative" }}>
- {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ?
+ {showChrome ?
<div className="collectionStackingView-addDocumentButton"
style={{
- width: style.columnWidth / style.numGroupColumns,
- padding: NumCast(this.props.parent.layoutDoc._yPadding)
+ //width: style.columnWidth / style.numGroupColumns,
+ padding: `${NumCast(this.props.parent.layoutDoc._yPadding, this.props.parent.yMargin)}px 0px 0px 0px`
}}>
<EditableView {...newEditableViewProps} />
</div> : null
@@ -258,7 +256,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
<div className={`collectionStackingView-masonryGrid`}
ref={this._contRef}
style={{
- padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`,
+ padding: stackPad,
width: this.props.parent.NodeWidth,
gridGap: this.props.parent.gridGap,
gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""),
diff --git a/src/client/views/collections/CollectionPileView.scss b/src/client/views/collections/CollectionPileView.scss
index ac874b663..48d07e42b 100644
--- a/src/client/views/collections/CollectionPileView.scss
+++ b/src/client/views/collections/CollectionPileView.scss
@@ -5,4 +5,7 @@
height: 100%;
width: 100%;
overflow: visible;
+ .collectionPileView-innards {
+ width:100%;
+ }
}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index d3ae21f3a..fc48e0327 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,18 +1,17 @@
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
+import { HeightSym, Opt, WidthSym, Doc } from "../../../fields/Doc";
+import { ScriptField } from "../../../fields/ScriptField";
+import { BoolCast, NumCast, StrCast } from "../../../fields/Types";
import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionPileView.scss";
import React = require("react");
import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
import { SelectionManager } from "../../util/SelectionManager";
-import { UndoManager } from "../../util/UndoManager";
+import { UndoManager, undoBatch } from "../../util/UndoManager";
import { SnappingManager } from "../../util/SnappingManager";
+import { DragManager } from "../../util/DragManager";
@observer
export class CollectionPileView extends CollectionSubView(doc => doc) {
@@ -20,10 +19,12 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
_doubleTap: boolean | undefined = false;
_originalChrome: string = "";
@observable _contentsActive = true;
- @observable _layoutEngine = "pass";
@observable _collapsed: boolean = false;
@observable _childClickedScript: Opt<ScriptField>;
componentDidMount() {
+ if (this.layoutEngine() !== "pass" && this.layoutEngine() !== "starburst") {
+ this.Document._pileLayoutEngine = "pass";
+ }
this._originalChrome = StrCast(this.layoutDoc._chromeStatus);
this.layoutDoc._chromeStatus = "disabled";
this.layoutDoc.hideFilterView = true;
@@ -33,49 +34,54 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
this.layoutDoc._chromeStatus = this._originalChrome;
}
- layoutEngine = () => this._layoutEngine;
+ layoutEngine = () => StrCast(this.Document._pileLayoutEngine);
@computed get contents() {
- return <div className="collectionPileView-innards" style={{
- width: "100%",
- pointerEvents: this.layoutEngine() !== "pass" && (this.props.active() || this.layoutEngine() === "starburst") ? undefined : "none"
- }} >
+ return <div className="collectionPileView-innards" style={{ pointerEvents: this.layoutEngine() === "starburst" ? undefined : "none" }} >
<CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine} />
</div>;
}
-
- specificMenu = (e: React.MouseEvent) => {
- const layoutItems: ContextMenuProps[] = [];
- const doc = this.props.Document;
-
- ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
- }
-
toggleStarburst = action(() => {
- if (this._layoutEngine === 'starburst') {
+ if (this.layoutEngine() === 'starburst') {
const defaultSize = 110;
this.layoutDoc._overflow = undefined;
+ this.childDocs.forEach(d => Doc.iconify(d));
this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2;
this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
- this._layoutEngine = 'pass';
+ Doc.pileup(this.childDocs);
+ this.layoutDoc._panX = 0;
+ this.layoutDoc._panY = -10;
+ this.props.Document._pileLayoutEngine = 'pass';
} else {
const defaultSize = 25;
this.layoutDoc._overflow = 'visible';
!this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500);
!this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5);
- if (this._layoutEngine === 'pass') {
+ if (this.layoutEngine() === 'pass') {
this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2;
this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym]();
this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym]();
}
+ this.layoutDoc._panX = this.layoutDoc._panY = 0;
this.layoutDoc._width = this.layoutDoc._height = defaultSize;
- this._layoutEngine = 'starburst';
+ this.props.Document._pileLayoutEngine = 'starburst';
}
});
+ @undoBatch
+ @action
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+ if (super.onInternalDrop(e, de)) {
+ if (de.complete.docDragData) {
+ Doc.pileup(this.childDocs);
+ }
+ }
+ return true;
+ }
+
_undoBatch: UndoManager.Batch | undefined;
pointerDown = (e: React.PointerEvent) => {
let dist = 0;
@@ -107,20 +113,17 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
}
onClick = (e: React.MouseEvent) => {
- if (e.button === 0 && (this._doubleTap || this.layoutEngine() === "starburst")) {
+ if (e.button === 0 && this._doubleTap) {
SelectionManager.DeselectAll();
this.toggleStarburst();
e.stopPropagation();
}
- // else if (this.layoutEngine() === "pass") {
- // runInAction(() => this._contentsActive = false);
- // setTimeout(action(() => this._contentsActive = true), 300);
- // }
}
render() {
- return <div className={"collectionPileView"} onContextMenu={this.specificMenu} onClick={this.onClick} onPointerDown={this.pointerDown}
+ return <div className={"collectionPileView"} onClick={this.onClick} onPointerDown={this.pointerDown}
+ ref={this.createDashEventsTarget}
style={{ width: this.props.PanelWidth(), height: `calc(100% - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
{this.contents}
</div>;
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 8a5450b0c..62aed67ed 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -4,8 +4,8 @@ import { observer } from "mobx-react";
import { CellInfo } from "react-table";
import "react-table/react-table.css";
import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils";
-import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
import { KeyCodes } from "../../util/KeyCodes";
import { SetupDrag, DragManager } from "../../util/DragManager";
import { CompileScript } from "../../util/Scripting";
@@ -16,11 +16,11 @@ import { EditableView } from "../EditableView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
import { CollectionView } from "./CollectionView";
-import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types";
+import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../fields/Types";
import { Docs } from "../../documents/Documents";
import { library } from '@fortawesome/fontawesome-svg-core';
import { faExpand } from '@fortawesome/free-solid-svg-icons';
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
import { SnappingManager } from "../../util/SnappingManager";
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 507ee89e4..dae0600b1 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -7,7 +7,7 @@ import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ColumnType } from "./CollectionSchemaView";
import { faFile } from "@fortawesome/free-regular-svg-icons";
-import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 5aec46a83..6f1e8ac1f 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -2,16 +2,16 @@ import React = require("react");
import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table";
import "./CollectionSchemaView.scss";
import { Transform } from "../../util/Transform";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../fields/Doc";
import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
-import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
+import { Cast, FieldValue, StrCast } from "../../../fields/Types";
import { ContextMenu } from "../ContextMenu";
import { action } from "mobx";
import { library } from '@fortawesome/fontawesome-svg-core';
import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { DocumentManager } from "../../util/DocumentManager";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
import { SnappingManager } from "../../util/SnappingManager";
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 51ad6c81b..35f892d65 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -6,13 +6,13 @@ import { action, computed, observable, untracked } from "mobx";
import { observer } from "mobx-react";
import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
import "react-table/react-table.css";
-import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { listSpec } from "../../../fields/Schema";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { ComputedField } from "../../../fields/ScriptField";
+import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../fields/Types";
import { Docs, DocumentOptions } from "../../documents/Documents";
import { CompileScript, Transformer, ts } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 5eaf29316..203c51163 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -7,11 +7,13 @@
.collectionStackingView {
display: flex;
}
+
.collectionStackingMasonry-cont {
- position:relative;
- height:100%;
- width:100%;
+ position: relative;
+ height: 100%;
+ width: 100%;
}
+
.collectionStackingView,
.collectionMasonryView {
height: 100%;
@@ -22,12 +24,17 @@
overflow-x: hidden;
flex-wrap: wrap;
transition: top .5s;
+
>div {
position: relative;
display: block;
}
+
.collectionStackingViewFieldColumn {
- height:max-content;
+ height: max-content;
+ }
+ .collectionStackingViewFieldColumnDragging {
+ height:100%;
}
.collectionSchemaView-previewDoc {
@@ -129,27 +136,34 @@
background: red;
}
}
+
.collectionStackingView-miniHeader {
width: 100%;
+
.editableView-container-editing-oneLine {
min-height: 20px;
display: flex;
align-items: center;
flex-direction: row;
}
- span::before , span::after{
+
+ span::before,
+ span::after {
content: "";
width: 50%;
border-top: dashed gray 1px;
position: relative;
display: inline-block;
}
+
span::before {
margin-right: 10px;
}
- span::after{
+
+ span::after {
margin-left: 10px;
}
+
span {
position: relative;
text-align: center;
@@ -157,10 +171,11 @@
overflow: visible;
width: 100%;
display: flex;
- color:gray;
+ color: gray;
align-items: center;
}
}
+
.collectionStackingView-sectionHeader {
text-align: center;
margin: auto;
@@ -277,7 +292,7 @@
height: 20px;
border-radius: 10px;
margin: 3px;
- width:max-content;
+ width: max-content;
&.active {
color: red;
@@ -294,15 +309,18 @@
display: none;
}
}
+
.collectionStackingView-sectionHeader:hover {
.collectionStackingView-sectionColor {
- display:unset;
+ display: unset;
}
+
.collectionStackingView-sectionOptions {
- display:unset;
+ display: unset;
}
+
.collectionStackingView-sectionDelete {
- display:unset;
+ display: unset;
}
}
@@ -403,4 +421,4 @@
.rc-switch-checked .rc-switch-inner {
left: 8px;
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 376e7c087..b84fc9266 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,14 +4,14 @@ import { CursorProperty } from "csstype";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import Switch from 'rc-switch';
-import { DataSym, Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec, makeInterface } from "../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
+import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc";
+import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { listSpec, makeInterface } from "../../../fields/Schema";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, smoothScroll } from "../../../Utils";
import { DragManager, dropActionType } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
@@ -26,6 +26,7 @@ import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewField
import { CollectionSubView } from "./CollectionSubView";
import { CollectionViewType } from "./CollectionView";
import { SnappingManager } from "../../util/SnappingManager";
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
const _global = (window /* browser */ || global /* node */) as any;
type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -194,8 +195,8 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
}
getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
- const layoutDoc = Doc.Layout(doc, this.props.ChildLayoutTemplate?.());
const height = () => this.getDocHeight(doc);
+ const opacity = () => this.Document.currentFrame === undefined ? this.props.childOpacity?.() : CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document.currentFrame))?.opacity;
return <ContentFittingDocumentView
Document={doc}
DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
@@ -210,11 +211,13 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
NativeHeight={returnZero}
NativeWidth={returnZero}
fitToBox={false}
+ dontRegisterView={this.props.dontRegisterView}
rootSelected={this.rootSelected}
dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
onClick={this.onChildClickHandler}
onDoubleClick={this.onChildDoubleClickHandler}
ScreenToLocalTransform={dxf}
+ opacity={opacity}
focus={this.focusDocument}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index dccef7983..a269b21f5 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -4,13 +4,13 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
-import { ImageField } from "../../../new_fields/URLField";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { RichTextField } from "../../../fields/RichTextField";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { ScriptField } from "../../../fields/ScriptField";
+import { NumCast, StrCast, Cast } from "../../../fields/Types";
+import { ImageField } from "../../../fields/URLField";
+import { TraceMobx } from "../../../fields/util";
import { Docs, DocUtils } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
@@ -21,7 +21,7 @@ import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
import "./CollectionStackingView.scss";
-import { listSpec } from "../../../new_fields/Schema";
+import { listSpec } from "../../../fields/Schema";
import { SnappingManager } from "../../util/SnappingManager";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
@@ -352,7 +352,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `;
const chromeStatus = this.props.parent.props.Document._chromeStatus;
return (
- <div className="collectionStackingViewFieldColumn" key={heading}
+ <div className={"collectionStackingViewFieldColumn" + (SnappingManager.GetIsDragging() ? "Dragging" : "")} key={heading}
style={{
width: `${100 / ((uniqueHeadings.length + ((chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`,
height: undefined, // DraggingManager.GetIsDragging() ? "100%" : undefined,
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
index 5b9a69bf7..c5c3f96e8 100644
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -1,7 +1,7 @@
import { CollectionSubView } from "./CollectionSubView";
import React = require("react");
import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-import { NumCast } from "../../../new_fields/Types";
+import { NumCast } from "../../../fields/Types";
import "./CollectionStaffView.scss";
import { observer } from "mobx-react";
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8b50bd8fc..423eb1d90 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,19 +1,19 @@
import { action, computed, IReactionDisposer, reaction } from "mobx";
import { basename } from 'path';
-import CursorField from "../../../new_fields/CursorField";
-import { Doc, Opt } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, ScriptCast } from "../../../new_fields/Types";
+import CursorField from "../../../fields/CursorField";
+import { Doc, Opt } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { listSpec } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, ScriptCast, NumCast } from "../../../fields/Types";
import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { Upload } from "../../../server/SharedMediaTypes";
import { Utils } from "../../../Utils";
import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
import { DocServer } from "../../DocServer";
-import { Docs, DocumentOptions } from "../../documents/Documents";
+import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { Networking } from "../../Network";
import { DragManager, dropActionType } from "../../util/DragManager";
@@ -25,6 +25,8 @@ import { FieldViewProps } from "../nodes/FieldView";
import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
import { CollectionView } from "./CollectionView";
import React = require("react");
+import { SelectionManager } from "../../util/SelectionManager";
+import { WebField } from "../../../fields/URLField";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc | Doc[]) => boolean;
@@ -44,6 +46,7 @@ export interface SubCollectionViewProps extends CollectionViewProps {
CollectionView: Opt<CollectionView>;
children?: never | (() => JSX.Element[]) | React.ReactNode;
ChildLayoutTemplate?: () => Doc;
+ childOpacity?: () => number;
ChildLayoutString?: string;
childClickScript?: ScriptField;
childDoubleClickScript?: ScriptField;
@@ -206,6 +209,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
}
+ addDocument = (doc: Doc | Doc[]) => this.props.addDocument(doc);
+
@undoBatch
@action
protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
@@ -213,22 +218,23 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
if (docDragData) {
let added = false;
- if (docDragData.dropAction || docDragData.userDropAction) {
- added = this.props.addDocument(docDragData.droppedDocuments);
+ const dropaction = docDragData.dropAction || docDragData.userDropAction;
+ if (dropaction && dropaction !== "move") {
+ added = this.addDocument(docDragData.droppedDocuments);
} else if (docDragData.moveDocument) {
const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
- const res = addedDocs.length ? this.props.addDocument(addedDocs) : true;
- added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.props.addDocument) : res;
+ const res = addedDocs.length ? this.addDocument(addedDocs) : true;
+ added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.addDocument) : res;
} else {
- added = this.props.addDocument(docDragData.droppedDocuments);
+ added = this.addDocument(docDragData.droppedDocuments);
}
e.stopPropagation();
return added;
}
else if (de.complete.annoDragData) {
e.stopPropagation();
- return this.props.addDocument(de.complete.annoDragData.dropDocument);
+ return this.addDocument(de.complete.annoDragData.dropDocument);
}
return false;
}
@@ -265,7 +271,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
e.stopPropagation();
e.preventDefault();
- const { addDocument } = this.props;
+ const { addDocument } = this;
if (!addDocument) {
alert("this.props.addDocument does not exist. Aborting drop operation.");
return;
@@ -321,9 +327,30 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
});
} else {
- const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300 });
+ let srcUrl: string | undefined;
+ let srcWeb: Doc | undefined;
+ if (SelectionManager.SelectedDocuments().length) {
+ srcWeb = SelectionManager.SelectedDocuments()[0].props.Document;
+ srcUrl = (srcWeb.data as WebField).url.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
+ }
+ const reg = new RegExp(Utils.prepend(""), "g");
+ const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
+ const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 });
Doc.GetProto(htmlDoc)["data-text"] = text;
this.props.addDocument(htmlDoc);
+ if (srcWeb) {
+ const focusNode = (SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")[0].contentDocument?.getSelection()?.focusNode as any);
+ if (focusNode) {
+ const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect();
+ const x = (rect?.x || 0);
+ const y = NumCast(srcWeb.scrollTop) + (rect?.y || 0);
+ const anchor = Docs.Create.FreeformDocument([], { _LODdisable: true, _backgroundColor: "transparent", _width: 25, _height: 25, x, y, annotationOn: srcWeb });
+ anchor.context = srcWeb;
+ const key = Doc.LayoutFieldKey(srcWeb);
+ Doc.AddDocToList(srcWeb, key + "-annotations", anchor);
+ DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor });
+ }
+ }
}
return;
}
@@ -332,7 +359,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
if (text) {
if (text.includes("www.youtube.com/watch")) {
- const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");
+ const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/").split("&")[0];
addDocument(Docs.Create.VideoDocument(url, {
...options,
title: url,
@@ -425,7 +452,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
if (generatedDocuments.length) {
const set = generatedDocuments.length > 1 && generatedDocuments.map(d => Doc.iconify(d));
if (set) {
- addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!));
+ addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!)!);
} else {
generatedDocuments.forEach(addDocument);
}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index a2d4774c8..15bc0bfd5 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,11 +1,11 @@
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Opt, DocCastAsync } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { ObjectField } from "../../../new_fields/ObjectField";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast, BoolCast, Cast } from "../../../new_fields/Types";
+import { Doc, Opt, DocCastAsync } from "../../../fields/Doc";
+import { List } from "../../../fields/List";
+import { ObjectField } from "../../../fields/ObjectField";
+import { RichTextField } from "../../../fields/RichTextField";
+import { ComputedField, ScriptField } from "../../../fields/ScriptField";
+import { NumCast, StrCast, BoolCast, Cast } from "../../../fields/Types";
import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
import { Scripting } from "../../util/Scripting";
import { ContextMenu } from "../ContextMenu";
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 2f332e77d..b2e1c0f73 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,13 +1,13 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { PrefetchProxy } from '../../../new_fields/Proxy';
-import { Document, listSpec } from '../../../new_fields/Schema';
-import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
+import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { PrefetchProxy } from '../../../fields/Proxy';
+import { Document, listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
@@ -32,7 +32,7 @@ import "./CollectionTreeView.scss";
import { CollectionViewType } from './CollectionView';
import React = require("react");
import { makeTemplate } from '../../util/DropConverter';
-import { TraceMobx } from '../../../new_fields/util';
+import { TraceMobx } from '../../../fields/util';
export interface TreeViewProps {
document: Doc;
@@ -717,7 +717,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
}
ContextMenu.Instance.addItem({
description: "Buxton Layout", icon: "eye", event: () => {
- const { ImageDocument } = Docs.Create;
+ const { ImageDocument, PdfDocument } = Docs.Create;
const { Document } = this.props;
const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
const detailView = Cast(Cast(Doc.UserDoc()["template-button-detail"], Doc, null)?.dragFactory, Doc, null);
@@ -726,13 +726,14 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
heroView._showTitle = "title";
heroView._showTitleHover = "titlehover";
- const doubleClickView = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 }); // replace with desired double click target
+ const fallback = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 }); // replace with desired double click target
+ let pdfContent: string;
DocListCast(this.dataDoc[this.props.fieldKey]).map(d => {
DocListCast(d.data).map((img, i) => {
const caption = (d.captions as any)[i];
if (caption) {
Doc.GetProto(img).caption = caption;
- Doc.GetProto(img).doubleClickView = doubleClickView;
+ Doc.GetProto(img).doubleClickView = (pdfContent = StrCast(img.additionalMedia_pdfs)) ? PdfDocument(pdfContent, { title: pdfContent }) : fallback;
}
});
Doc.GetProto(d).type = "buxton";
@@ -748,7 +749,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
Document.childLayoutTemplate = heroView;
Document.childClickedOpenTemplateView = new PrefetchProxy(detailView);
Document._viewType = CollectionViewType.Time;
- Document._forceActive = true;
+ Document.forceActive = true;
Document._pivotField = "company";
Document.childDropAction = "alias";
}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0f239d385..7acb3457b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -7,12 +7,12 @@ import { observer } from "mobx-react";
import * as React from 'react';
import Lightbox from 'react-image-lightbox-with-rotate';
import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
-import { DateField } from '../../../new_fields/DateField';
-import { DataSym, Doc, DocListCast, Field, Opt } from '../../../new_fields/Doc';
-import { List } from '../../../new_fields/List';
-import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
-import { ImageField } from '../../../new_fields/URLField';
-import { TraceMobx } from '../../../new_fields/util';
+import { DateField } from '../../../fields/DateField';
+import { DataSym, Doc, DocListCast, Field, Opt, AclSym, AclAddonly, AclReadonly } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../fields/Types';
+import { ImageField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils';
import { DocumentType } from '../../documents/DocumentTypes';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
@@ -35,13 +35,13 @@ import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from "./CollectionTreeView";
import './CollectionView.scss';
import { CollectionViewBaseChrome } from './CollectionViewChromes';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { listSpec } from '../../../new_fields/Schema';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { Id } from '../../../fields/FieldSymbols';
+import { listSpec } from '../../../fields/Schema';
import { Docs } from '../../documents/Documents';
-import { ScriptField, ComputedField } from '../../../new_fields/ScriptField';
+import { ScriptField, ComputedField } from '../../../fields/ScriptField';
import { InteractionUtils } from '../../util/InteractionUtils';
-import { ObjectField } from '../../../new_fields/ObjectField';
+import { ObjectField } from '../../../fields/ObjectField';
import CollectionMapView from './CollectionMapView';
import { CollectionPileView } from './CollectionPileView';
const higflyout = require("@hig/flyout");
@@ -69,9 +69,10 @@ export enum CollectionViewType {
Pile = "pileup"
}
export interface CollectionViewCustomProps {
- filterAddDocument: (doc: Doc | Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+ filterAddDocument?: (doc: Doc | Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
childLayoutTemplate?: () => Opt<Doc>; // specify a layout Doc template to use for children of the collection
childLayoutString?: string; // specify a layout string to use for children of the collection
+ childOpacity?: () => number;
}
export interface CollectionRenderProps {
@@ -117,20 +118,24 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
@action.bound
addDocument = (doc: Doc | Doc[]): boolean => {
- if (doc instanceof Doc) {
- if (this.props.filterAddDocument?.(doc) === false) {
- return false;
- }
+ if (this.props.filterAddDocument?.(doc) === false) {
+ return false;
}
const docs = doc instanceof Doc ? [doc] : doc;
const targetDataDoc = this.props.Document[DataSym];
const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
const added = docs.filter(d => !docList.includes(d));
if (added.length) {
- added.map(doc => doc.context = this.props.Document);
- added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
- targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
- targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ if (this.dataDoc[AclSym] === AclReadonly) {
+ return false;
+ } else if (this.dataDoc[AclSym] === AclAddonly) {
+ added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc));
+ } else {
+ added.map(doc => doc.context = this.props.Document);
+ added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
+ targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
+ targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ }
}
return true;
}
@@ -494,7 +499,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
return (<div className={"collectionView"}
style={{
pointerEvents: this.props.Document.isBackground ? "none" : undefined,
- boxShadow: this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
+ boxShadow: Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
`${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31)" : "#9c9396"} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
}}
onContextMenu={this.onContextMenu}>
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index e4581eb46..03bd9a01a 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -8,10 +8,12 @@
z-index: 9001;
transition: top .5s;
background: lightgrey;
+ transform-origin: top left;
.collectionViewChrome {
display: flex;
- padding-bottom: 10px;
+ padding-bottom: 1px;
+ height:32px;
border-bottom: .5px solid rgb(180, 180, 180);
overflow: hidden;
@@ -21,12 +23,12 @@
.collectionViewBaseChrome-viewPicker {
font-size: 75%;
//text-transform: uppercase;
- letter-spacing: 2px;
+ //letter-spacing: 2px;
background: rgb(238, 238, 238);
color: grey;
outline-color: black;
border: none;
- padding: 12px 10px 11px 10px;
+ //padding: 12px 10px 11px 10px;
}
.collectionViewBaseChrome-viewPicker:active {
@@ -47,6 +49,7 @@
.collectionViewBaseChrome-cmdPicker {
margin-left: 3px;
margin-right: 0px;
+ font-size: 75%;
background: rgb(238, 238, 238);
border: none;
color: grey;
@@ -56,6 +59,7 @@
background-color: gray;
display: flex;
flex-direction: row;
+ height:30px;
.commandEntry-drop {
color:white;
width:25px;
@@ -95,8 +99,8 @@
margin: auto;
background: gray;
color: white;
- width: 40px;
- height: 40px;
+ width: 30px;
+ height: 30px;
align-items: center;
justify-content: center;
}
@@ -195,8 +199,7 @@
.collectionTreeViewChrome-pivotField-label {
vertical-align: center;
padding-left: 10px;
- padding-top: 10px;
- padding-bottom: 10px;
+ margin:auto;
}
.collectionStackingViewChrome-pivotField,
@@ -204,14 +207,15 @@
color: white;
width:100%;
min-width: 100px;
- text-align: center;
+ display: flex;
+ align-items: center;
background: rgb(238, 238, 238);
.editable-view-input,
input,
.editableView-container-editing-oneLine,
.editableView-container-editing {
- padding: 12px 10px 11px 10px;
+ margin:auto;
border: 0px;
color: grey;
text-align: center;
@@ -235,6 +239,44 @@
}
}
+.collectionFreeFormViewChrome-cont {
+ width: 60px;
+ display: flex;
+ position: relative;
+ align-items: center;
+ .fwdKeyframe, .numKeyframe, .backKeyframe {
+ cursor: pointer;
+ position: absolute;
+ width: 20;
+ height: 30;
+ bottom: 0;
+ background: gray;
+ display: flex;
+ align-items: center;
+ color:white;
+ }
+ .backKeyframe {
+ left:0;
+ svg {
+ display:block;
+ margin:auto;
+ }
+ }
+ .numKeyframe {
+ left:20;
+ display: flex;
+ flex-direction: column;
+ padding: 5px;
+ }
+ .fwdKeyframe {
+ left:40;
+ svg {
+ display:block;
+ margin:auto;
+ }
+ }
+}
+
.collectionSchemaViewChrome-cont {
display: flex;
font-size: 10.5px;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 62b03bbdc..29a3e559a 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -2,11 +2,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { listSpec } from "../../../fields/Schema";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
import { Utils, emptyFunction, setupMoveUpEvents } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
import { undoBatch } from "../../util/UndoManager";
@@ -15,6 +15,7 @@ import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
import { CollectionViewType } from "./CollectionView";
import { CollectionView } from "./CollectionView";
import "./CollectionViewChromes.scss";
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
const datepicker = require('js-datepicker');
interface CollectionViewChromeProps {
@@ -44,7 +45,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
initialize: emptyFunction,
};
_narrativeCommand = {
- params: ["target", "source"], title: "=> click clicked open view",
+ params: ["target", "source"], title: "=> child click view",
script: "this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])",
immediate: (source: Doc[]) => this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]),
initialize: emptyFunction,
@@ -83,22 +84,16 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
private _viewRef = React.createRef<HTMLInputElement>();
@observable private _currentKey: string = "";
- componentDidMount = () => {
- runInAction(() => {
- // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
- const chromeStatus = this.props.CollectionView.props.Document._chromeStatus;
- if (chromeStatus) {
- if (chromeStatus === "disabled") {
- throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!");
- }
- else if (chromeStatus === "collapsed") {
- if (this.props.collapse) {
- this.props.collapse(true);
- }
- }
- }
- });
- }
+ componentDidMount = action(() => {
+ // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
+ switch (this.props.CollectionView.props.Document._chromeStatus) {
+ case "disabled":
+ throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!");
+ case "collapsed":
+ this.props.collapse?.(true);
+ break;
+ }
+ })
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
@@ -197,10 +192,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
}
}
- subChrome = () => {
+ @computed get subChrome() {
const collapsed = this.document._chromeStatus !== "enabled";
if (collapsed) return null;
switch (this.props.type) {
+ case CollectionViewType.Freeform: return (<CollectionFreeFormViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
@@ -236,7 +232,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
const vtype = this.props.CollectionView.collectionViewType;
const c = {
params: ["target"], title: vtype,
- script: `this.target._viewType = ${StrCast(this.props.CollectionView.props.Document._viewType)}`,
+ script: `this.target._viewType = '${StrCast(this.props.CollectionView.props.Document._viewType)}'`,
immediate: (source: Doc[]) => this.props.CollectionView.props.Document._viewType = Doc.getDocTemplate(source?.[0]),
initialize: emptyFunction,
};
@@ -254,14 +250,61 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
}, emptyFunction, emptyFunction);
}
+ @computed get templateChrome() {
+ const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
+ return <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
+ <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
+ <div className="commandEntry-drop">
+ <FontAwesomeIcon icon="bullseye" size="2x" />
+ </div>
+ <select
+ className="collectionViewBaseChrome-cmdPicker"
+ onPointerDown={stopPropagation}
+ onChange={this.commandChanged}
+ value={this._currentKey}>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""} />
+ {this._buttonizableCommands.map(cmd =>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
+ )}
+ </select>
+ </div>
+ </div>;
+ }
+
+ @computed get viewModes() {
+ const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
+ return <div className="collectionViewBaseChrome-viewModes" style={{ display: collapsed ? "none" : undefined }}>
+ <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._viewRef} onPointerDown={this.dragViewDown}>
+ <div className="commandEntry-drop">
+ <FontAwesomeIcon icon="bullseye" size="2x" />
+ </div>
+ <select
+ className="collectionViewBaseChrome-viewPicker"
+ onPointerDown={stopPropagation}
+ onChange={this.viewChanged}
+ value={StrCast(this.props.CollectionView.props.Document._viewType)}>
+ {Object.values(CollectionViewType).map(type => ["invalid", "docking"].includes(type) ? (null) : (
+ <option
+ key={Utils.GenerateGuid()}
+ className="collectionViewBaseChrome-viewOption"
+ onPointerDown={stopPropagation}
+ value={type}>
+ {type[0].toUpperCase() + type.substring(1)}
+ </option>
+ ))}
+ </select>
+ </div>
+ </div>;
+ }
+
render() {
const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
+ const scale = Math.min(1, this.props.CollectionView.props.ScreenToLocalTransform().Scale);
return (
<div className="collectionViewChrome-cont" style={{
top: collapsed ? -70 : 0, height: collapsed ? 0 : undefined,
- transform: collapsed ? "" : `scale(${Math.min(1, this.props.CollectionView.props.ScreenToLocalTransform().Scale)})`,
- transformOrigin: "top left",
- width: `${this.props.PanelWidth() / Math.min(1, this.props.CollectionView.props.ScreenToLocalTransform().Scale)}px`
+ transform: collapsed ? "" : `scale(${scale})`,
+ width: `${this.props.PanelWidth() / scale}px`
}}>
<div className="collectionViewChrome" style={{ border: "unset", pointerEvents: collapsed ? "none" : undefined }}>
<div className="collectionViewBaseChrome">
@@ -276,52 +319,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
title="Collapse collection chrome" onClick={this.toggleCollapse}>
<FontAwesomeIcon icon="caret-up" size="2x" />
</button>
- <div className="collectionViewBaseChrome-viewModes" style={{ display: collapsed ? "none" : undefined }}>
- <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._viewRef} onPointerDown={this.dragViewDown}>
- <div className="commandEntry-drop">
- <FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
- </div>
- <select
- className="collectionViewBaseChrome-viewPicker"
- onPointerDown={stopPropagation}
- onChange={this.viewChanged}
- value={StrCast(this.props.CollectionView.props.Document._viewType)}>
- {Object.values(CollectionViewType).map(type => ["invalid", "docking"].includes(type) ? (null) : (
- <option
- key={Utils.GenerateGuid()}
- className="collectionViewBaseChrome-viewOption"
- onPointerDown={stopPropagation}
- value={type}>
- {type[0].toUpperCase() + type.substring(1)}
- </option>
- ))}
- </select>
- </div>
- </div>
+ {this.viewModes}
<div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
<div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.toggleViewSpecs} >
<FontAwesomeIcon icon="filter" size="2x" />
</div>
</div>
- <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
- <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
- <div className="commandEntry-drop">
- <FontAwesomeIcon icon="bullseye" size="2x" />
- </div>
- <select
- className="collectionViewBaseChrome-cmdPicker"
- onPointerDown={stopPropagation}
- onChange={this.commandChanged}
- value={this._currentKey}>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""}>{""}</option>
- {this._buttonizableCommands.map(cmd =>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
- )}
- </select>
- </div>
- </div>
+ {this.templateChrome}
</div>
- {this.subChrome()}
+ {this.subChrome}
</div>
</div>
);
@@ -329,6 +335,56 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
}
@observer
+export class CollectionFreeFormViewChrome extends React.Component<CollectionViewChromeProps> {
+
+ get Document() { return this.props.CollectionView.props.Document; }
+ @computed get dataField() {
+ return this.props.CollectionView.props.Document[Doc.LayoutFieldKey(this.props.CollectionView.props.Document)];
+ }
+ @computed get childDocs() {
+ return DocListCast(this.dataField);
+ }
+ @undoBatch
+ @action
+ nextKeyframe = (): void => {
+ const currentFrame = NumCast(this.Document.currentFrame);
+ if (currentFrame === undefined) {
+ this.Document.currentFrame = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
+ }
+ CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0);
+ this.Document.currentFrame = Math.max(0, (currentFrame || 0) + 1);
+ this.Document.lastFrame = Math.max(NumCast(this.Document.currentFrame), NumCast(this.Document.lastFrame));
+ }
+ @undoBatch
+ @action
+ prevKeyframe = (): void => {
+ const currentFrame = NumCast(this.Document.currentFrame);
+ if (currentFrame === undefined) {
+ this.Document.currentFrame = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
+ }
+ CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
+ this.Document.currentFrame = Math.max(0, (currentFrame || 0) - 1);
+ }
+ render() {
+ return this.Document.isAnnotationOverlay ? (null) :
+ <div className="collectionFreeFormViewChrome-cont">
+ <div key="back" title="back frame" className="backKeyframe" onClick={this.prevKeyframe}>
+ <FontAwesomeIcon icon={"caret-left"} size={"lg"} />
+ </div>
+ <div key="num" title="toggle view all" className="numKeyframe" style={{ backgroundColor: this.Document.editing ? "#759c75" : "#c56565" }}
+ onClick={action(() => this.Document.editing = !this.Document.editing)} >
+ {NumCast(this.Document.currentFrame)}
+ </div>
+ <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={this.nextKeyframe}>
+ <FontAwesomeIcon icon={"caret-right"} size={"lg"} />
+ </div>
+ </div>;
+ }
+}
+
+@observer
export class CollectionStackingViewChrome extends React.Component<CollectionViewChromeProps> {
@observable private _currentKey: string = "";
@observable private suggestions: string[] = [];
@@ -371,6 +427,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
this.suggestions = [];
}
+ @action
setValue = (value: string) => {
this.props.CollectionView.props.Document._pivotField = value;
return true;
@@ -473,19 +530,20 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
@observer
export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> {
- get dataExtension() {
- return this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey + "_ext"] as Doc;
+ get sortAscending() {
+ return this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey + "-sortAscending"];
}
- @computed private get descending() {
- return this.dataExtension && Cast(this.dataExtension.sortAscending, "boolean", null);
+ set sortAscending(value) {
+ this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey + "-sortAscending"] = value;
+ }
+ @computed private get ascending() {
+ return Cast(this.sortAscending, "boolean", null);
}
@action toggleSort = () => {
- if (this.dataExtension) {
- if (this.dataExtension.sortAscending) this.dataExtension.sortAscending = undefined;
- else if (this.dataExtension.sortAscending === undefined) this.dataExtension.sortAscending = false;
- else this.dataExtension.sortAscending = true;
- }
+ if (this.sortAscending) this.sortAscending = undefined;
+ else if (this.sortAscending === undefined) this.sortAscending = false;
+ else this.sortAscending = true;
}
render() {
@@ -495,7 +553,7 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewChro
<div className="collectionTreeViewChrome-sortLabel">
Sort
</div>
- <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.descending === undefined ? "90" : this.descending ? "180" : "0"}deg)` }}>
+ <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? "90" : this.ascending ? "180" : "0"}deg)` }}>
<FontAwesomeIcon icon="caret-up" size="2x" color="white" />
</div>
</button>
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 6e4f801c0..649406e6c 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -1,12 +1,12 @@
import * as React from "react";
import './ParentDocumentSelector.scss';
-import { Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../fields/Doc";
import { observer } from "mobx-react";
import { observable, action, runInAction, trace, computed, reaction, IReactionDisposer } from "mobx";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
import { SearchUtil } from "../../util/SearchUtil";
import { CollectionDockingView } from "./CollectionDockingView";
-import { NumCast, StrCast } from "../../../new_fields/Types";
+import { NumCast, StrCast } from "../../../fields/Types";
import { CollectionViewType } from "./CollectionView";
import { DocumentButtonBar } from "../DocumentButtonBar";
import { DocumentManager } from "../../util/DocumentManager";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 9a864078a..a4fd5384f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,15 +1,15 @@
-import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, Cast } from "../../../../new_fields/Types";
+import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../fields/Doc";
+import { NumCast, StrCast, Cast } from "../../../../fields/Types";
import { ScriptBox } from "../../ScriptBox";
import { CompileScript } from "../../../util/Scripting";
-import { ScriptField } from "../../../../new_fields/ScriptField";
+import { ScriptField } from "../../../../fields/ScriptField";
import { OverlayView, OverlayElementOptions } from "../../OverlayView";
import { emptyFunction, aggregateBounds } from "../../../../Utils";
import React = require("react");
-import { Id, ToString } from "../../../../new_fields/FieldSymbols";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { RefField } from "../../../../new_fields/RefField";
-import { listSpec } from "../../../../new_fields/Schema";
+import { Id, ToString } from "../../../../fields/FieldSymbols";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { RefField } from "../../../../fields/RefField";
+import { listSpec } from "../../../../fields/Schema";
export interface ViewDefBounds {
type: string;
@@ -25,6 +25,7 @@ export interface ViewDefBounds {
fontSize?: number;
highlight?: boolean;
color?: string;
+ opacity?: number;
replica?: string;
pair?: { layout: Doc, data?: Doc };
}
@@ -37,6 +38,7 @@ export interface PoolData {
width?: number;
height?: number;
color?: string;
+ opacity?: number;
transition?: string;
highlight?: boolean;
replica: string;
@@ -416,7 +418,7 @@ function normalizeResults(
height: newPosRaw.height! * scale,
pair: ele[1].pair
};
- poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "transform 1s", ...newPos });
+ poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "all 1s", ...newPos });
}
});
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index ba71aff32..f3fc04752 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,5 +1,5 @@
import { observer } from "mobx-react";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
import { Utils } from '../../../../Utils';
import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinkView.scss";
@@ -7,8 +7,8 @@ import React = require("react");
import v5 = require("uuid/v5");
import { DocumentType } from "../../../documents/DocumentTypes";
import { observable, action, reaction, IReactionDisposer } from "mobx";
-import { StrCast, Cast } from "../../../../new_fields/Types";
-import { Id } from "../../../../new_fields/FieldSymbols";
+import { StrCast, Cast } from "../../../../fields/Types";
+import { Id } from "../../../../fields/FieldSymbols";
import { SnappingManager } from "../../../util/SnappingManager";
export interface CollectionFreeFormLinkViewProps {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 1208fb324..ae81b4b36 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,7 +1,7 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
+import { Doc } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
import { DocumentManager } from "../../../util/DocumentManager";
import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinksView.scss";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 92fa2781c..548ad78a5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,16 +1,16 @@
import { observer } from "mobx-react";
import * as mobxUtils from 'mobx-utils';
-import CursorField from "../../../../new_fields/CursorField";
-import { listSpec } from "../../../../new_fields/Schema";
-import { Cast } from "../../../../new_fields/Types";
-import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
+import CursorField from "../../../../fields/CursorField";
+import { listSpec } from "../../../../fields/Schema";
+import { Cast } from "../../../../fields/Types";
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { CollectionViewProps } from "../CollectionSubView";
import "./CollectionFreeFormView.scss";
import React = require("react");
import v5 = require("uuid/v5");
import { computed } from "mobx";
-import { FieldResult } from "../../../../new_fields/Doc";
-import { List } from "../../../../new_fields/List";
+import { FieldResult } from "../../../../fields/Doc";
+import { List } from "../../../../fields/List";
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 60c39c825..d9011c9d3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -41,6 +41,7 @@
// touch action none means that the browser will handle none of the touch actions. this allows us to implement our own actions.
touch-action: none;
+
.collectionfreeformview-placeholder {
background: gray;
width: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 6caee960d..c753a703d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,24 +1,24 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";
import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, _allowStateChangesInsideComputed } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, _allowStateChangesInsideComputed, trace } from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
-import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc";
-import { documentSchema, collectionSchema } from "../../../../new_fields/documentSchemas";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField";
-import { List } from "../../../../new_fields/List";
-import { RichTextField } from "../../../../new_fields/RichTextField";
-import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
-import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
-import { TraceMobx } from "../../../../new_fields/util";
+import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../fields/Doc";
+import { documentSchema, collectionSchema } from "../../../../fields/documentSchemas";
+import { Id } from "../../../../fields/FieldSymbols";
+import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkField";
+import { List } from "../../../../fields/List";
+import { RichTextField } from "../../../../fields/RichTextField";
+import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema";
+import { ScriptField, ComputedField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
+import { TraceMobx } from "../../../../fields/util";
import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
-import { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse, numberRange } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
-import { Docs } from "../../../documents/Documents";
+import { Docs, DocUtils } from "../../../documents/Documents";
import { DocumentManager } from "../../../util/DocumentManager";
import { DragManager, dropActionType } from "../../../util/DragManager";
import { HistoryUtil } from "../../../util/History";
@@ -46,6 +46,7 @@ import React = require("react");
import { CollectionViewType } from "../CollectionView";
import { Timeline } from "../../animationtimeline/Timeline";
import { SnappingManager } from "../../../util/SnappingManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -53,6 +54,9 @@ export const panZoomSchema = createSchema({
_panX: "number",
_panY: "number",
scale: "number",
+ currentTimecode: "number",
+ displayTimecode: "number",
+ currentFrame: "number",
arrangeScript: ScriptField,
arrangeInit: ScriptField,
useClusters: "boolean",
@@ -72,6 +76,7 @@ const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentS
export type collectionFreeformViewProps = {
forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
viewDefDivClick?: ScriptField;
+ childPointerEvents?: boolean;
};
@observer
@@ -113,27 +118,66 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) :
this.Document.scale || 1)
- private centeringShiftX = () => !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / this.contentScaling : 0; // shift so pan position is at center of window for non-overlay collections
- private centeringShiftY = () => !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / this.contentScaling : 0;// shift so pan position is at center of window for non-overlay collections
- private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
- private getTransformOverlay = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1);
- private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
- private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
+ @computed get cachedCenteringShiftX(): number {
+ return !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / this.contentScaling : 0; // shift so pan position is at center of window for non-overlay collections
+ }
+ @computed get cachedCenteringShiftY(): number {
+ return !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / this.contentScaling : 0;// shift so pan position is at center of window for non-overlay collections
+ }
+ @computed get cachedGetLocalTransform(): Transform {
+ return Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
+ }
+ @computed get cachedGetContainerTransform(): Transform {
+ return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
+ }
+ @computed get cachedGetTransform(): Transform {
+ return this.getTransformOverlay().translate(- this.cachedCenteringShiftX, - this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
+ }
+
+ private centeringShiftX = () => this.cachedCenteringShiftX;
+ private centeringShiftY = () => this.cachedCenteringShiftY;
+ private getTransform = () => this.cachedGetTransform.copy();
+ private getLocalTransform = () => this.cachedGetLocalTransform.copy();
+ private getContainerTransform = () => this.cachedGetContainerTransform.copy();
+ private getTransformOverlay = () => this.getContainerTransform().translate(1, 1);
private addLiveTextBox = (newBox: Doc) => {
FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newBox);
}
- private addDocument = (newBox: Doc | Doc[]) => {
+ addDocument = action((newBox: Doc | Doc[]) => {
+ let retVal = false;
if (newBox instanceof Doc) {
- const added = this.props.addDocument(newBox);
- added && this.bringToFront(newBox);
- added && this.updateCluster(newBox);
- return added;
+ retVal = this.props.addDocument(newBox);
+ retVal && this.bringToFront(newBox);
+ retVal && this.updateCluster(newBox);
} else {
- return this.props.addDocument(newBox);
+ retVal = this.props.addDocument(newBox);
// bcz: deal with clusters
}
- }
+ if (retVal) {
+ const newBoxes = (newBox instanceof Doc) ? [newBox] : newBox;
+ for (let i = 0; i < newBoxes.length; i++) {
+ const newBox = newBoxes[i];
+ if (newBox.activeFrame !== undefined) {
+ const x = newBox.x;
+ const y = newBox.y;
+ delete newBox["x-indexed"];
+ delete newBox["y-indexed"];
+ delete newBox["opacity-indexed"];
+ delete newBox.x;
+ delete newBox.y;
+ delete newBox.activeFrame;
+ newBox.x = x;
+ newBox.y = y;
+ }
+ }
+ if (this.Document.currentFrame !== undefined && !this.props.isAnnotationOverlay) {
+ CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document.currentFrame);
+ }
+ }
+ return retVal;
+ })
+
private selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
@@ -159,6 +203,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const [xp, yp] = xf.transformPoint(de.x, de.y);
const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+ if (!this.isAnnotationOverlay && de.complete.linkDragData && de.complete.linkDragData.linkSourceDocument !== this.props.Document) {
+ const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
+ this.props.addDocument(source);
+ (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceDocument },
+ "doc annotation")); // TODODO this is where in text links get passed
+ e.stopPropagation();
+ return true;
+ }
if (super.onInternalDrop(e, de)) {
if (de.complete.docDragData) {
if (de.complete.docDragData.droppedDocuments.length) {
@@ -174,8 +226,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
for (let i = 0; i < droppedDocs.length; i++) {
const d = droppedDocs[i];
const layoutDoc = Doc.Layout(d);
- d.x = x + NumCast(d.x) - dropX;
- d.y = y + NumCast(d.y) - dropY;
+ if (this.Document.currentFrame !== undefined && !this.props.isAnnotationOverlay) {
+ const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
+ CollectionFreeFormDocumentView.setValues(this.Document.currentFrame, d, x + vals.x - dropX, y + vals.y - dropY, vals.opacity);
+ } else {
+ d.x = x + NumCast(d.x) - dropX;
+ d.y = y + NumCast(d.y) - dropY;
+ }
if (!NumCast(layoutDoc._width)) {
layoutDoc._width = 300;
}
@@ -401,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
case GestureUtils.Gestures.Stroke:
const points = ge.points;
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height });
+ const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, InkingControl.Instance.selectedWidth, points, { title: "ink stroke", x: B.x, y: B.y, _width: B.width, _height: B.height });
this.addDocument(inkDoc);
e.stopPropagation();
break;
@@ -437,9 +494,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
console.log("end");
if (this._inkToTextStartX && this._inkToTextStartY) {
const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
- const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === "text" && s.color);
+ const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === "rtf" && s.color);
const sets = setDocs.map((sd) => {
- return Cast(sd.data, RichTextField)?.Text as string;
+ return Cast(sd.text, RichTextField)?.Text as string;
});
if (sets.length && sets[0]) {
this._wordPalette.clear();
@@ -475,6 +532,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
});
+ console.log(this._wordPalette)
CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
console.log(results);
const wordResults = results.filter((r: any) => r.category === "inkWord");
@@ -733,7 +791,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.props.Document.lockedTransform || this.props.Document.inOverlay) return;
+ if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay) return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
}
@@ -769,7 +827,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
}
}
- if (!this.Document.lockedTransform || this.Document.inOverlay) {
+ if (!this.layoutDoc._lockedTransform || this.Document.inOverlay) {
this.Document.panTransformType = panType;
const scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
@@ -893,6 +951,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
FreezeDimensions: this.props.freezeChildDimensions,
layoutKey: undefined,
setupDragLines: this.setupDragLines,
+ dontRegisterView: this.props.dontRegisterView,
rootSelected: childData ? this.rootSelected : returnFalse,
dropAction: StrCast(this.props.Document.childDropAction) as dropActionType,
onClick: this.onChildClickHandler,
@@ -941,9 +1000,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return { x: 0, y: 0, transition: "transform 1s", ...result, pair: params.pair, replica: "" };
}
const layoutDoc = Doc.Layout(params.pair.layout);
- const { x, y, z, color, zIndex } = params.pair.layout;
+ const { x, y, opacity } = this.Document.currentFrame === undefined ? params.pair.layout :
+ CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document.currentFrame || 0);
+ const { z, color, zIndex } = params.pair.layout;
return {
x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
+ transition: StrCast(layoutDoc.transition), opacity: this.Document.editing ? 1 : Cast(opacity, "number", null),
width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number"), pair: params.pair, replica: ""
};
}
@@ -1003,8 +1065,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
doFreeformLayout(poolData: Map<string, PoolData>) {
const layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log);
- const state = initResult && initResult.success ? initResult.result.scriptState : undefined;
- const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
+ const state = initResult?.success ? initResult.result.scriptState : undefined;
+ const elements = initResult?.success ? this.viewDefsToJSX(initResult.result.views) : [];
this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
const pos = this.getCalculatedPositions({ pair, index: i, collection: this.Document, docs: layoutDocs, state });
@@ -1032,11 +1094,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const { newPool, computedElementData } = this.doInternalLayoutComputation;
const array = Array.from(newPool.entries());
runInAction(() => {
- for (let i = 0; i < array.length; i++) {
- const entry = array[i];
+ for (const entry of array) {
const lastPos = this._cachedPool.get(entry[0]); // last computed pos
const newPos = entry[1];
- if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) {
+ if (!lastPos || newPos.opacity !== lastPos.opacity || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) {
this._layoutPoolData.set(entry[0], newPos);
}
if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
@@ -1056,11 +1117,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
replica={entry[1].replica}
dataProvider={this.childDataProvider}
sizeProvider={this.childSizeProvider}
- pointerEvents={
- this.backgroundActive ?
- true :
- (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? false : undefined}
- jitterRotation={NumCast(this.props.Document._jitterRotation)}
+ pointerEvents={this.backgroundActive || this.props.childPointerEvents ?
+ true :
+ (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? false : undefined}
+ jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))}
//fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
fitToBox={BoolCast(this.props.freezeChildDimensions)} // bcz: check this
FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
@@ -1130,6 +1190,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
}
+ @undoBatch
+ @action
+ toggleLockTransform = (): void => {
+ this.layoutDoc._lockedTransform = this.layoutDoc._lockedTransform ? undefined : true;
+ }
+
private thumbIdentifier?: number;
onContextMenu = (e: React.MouseEvent) => {
@@ -1145,11 +1211,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
+ optionItems.push({ description: "toggle snap line display", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" });
optionItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" });
+ optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
// layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
optionItems.push({
@@ -1275,8 +1343,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getContainerTransform={this.getContainerTransform}
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
- <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY} shifted={!this.nativeHeight && !this.isAnnotationOverlay}
- easing={this.easing} viewDefDivClick={this.props.viewDefDivClick} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
+ <CollectionFreeFormViewPannableContents
+ centeringShiftX={this.centeringShiftX}
+ centeringShiftY={this.centeringShiftY}
+ shifted={!this.nativeHeight && !this.isAnnotationOverlay}
+ easing={this.easing}
+ transition={Cast(this.layoutDoc.transition, "string", null)}
+ viewDefDivClick={this.props.viewDefDivClick}
+ zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
{this.children}
</CollectionFreeFormViewPannableContents>
{this._timelineVisible ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)}
@@ -1295,18 +1369,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
render() {
TraceMobx();
const clientRect = this._mainCont?.getBoundingClientRect();
- // update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
- // this.Document.fitX = this.contentBounds && this.contentBounds.x;
- // this.Document.fitY = this.contentBounds && this.contentBounds.y;
- // this.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x);
- // this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
- // if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey.
- // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document
- return <div className={"collectionfreeformview-container"}
- ref={this.createDashEventsTarget}
+ return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget}
onPointerOver={this.onPointerOver}
- onWheel={this.onPointerWheel} onClick={this.onClick} //pointerEvents: DraggingManager.GetIsDragging() ? "all" : undefined,
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
+ onWheel={this.onPointerWheel}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}
+ onPointerMove={this.onCursorMove}
+ onDrop={this.onExternalDrop.bind(this)}
+ onDragOver={e => {
+ e.preventDefault();
+ }}
+ onContextMenu={this.onContextMenu}
style={{
pointerEvents: this.backgroundEvents ? "all" : undefined,
transform: this.contentScaling ? `scale(${this.contentScaling})` : "",
@@ -1322,7 +1395,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
style={{
display: this._pullDirection ? "block" : "none",
top: clientRect ? this._pullDirection === "bottom" ? this._pullCoords[1] - clientRect.y : 0 : "auto",
- // left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x - MainView.Instance.flyoutWidth : 0 : "auto",
left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x : 0 : "auto",
width: clientRect ? this._pullDirection === "left" ? this._pullCoords[0] - clientRect.left : this._pullDirection === "right" ? clientRect.right - this._pullCoords[0] : clientRect.width : 0,
height: clientRect ? this._pullDirection === "top" ? this._pullCoords[1] - clientRect.top : this._pullDirection === "bottom" ? clientRect.bottom - this._pullCoords[1] : clientRect.height : 0,
@@ -1361,6 +1433,7 @@ interface CollectionFreeFormViewPannableContentsProps {
viewDefDivClick?: ScriptField;
children: () => JSX.Element[];
shifted: boolean;
+ transition?: string;
}
@observer
@@ -1375,7 +1448,8 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
return <div className={freeformclass}
style={{
width: this.props.shifted ? 0 : undefined, height: this.props.shifted ? 0 : undefined,
- transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)`
+ transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)`,
+ transition: this.props.transition
}}>
{this.props.children()}
</div>;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8e20b39d2..cdfeeaa6b 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,11 +1,11 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../../new_fields/Doc";
-import { InkData, InkField } from "../../../../new_fields/InkField";
-import { List } from "../../../../new_fields/List";
-import { RichTextField } from "../../../../new_fields/RichTextField";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc, Opt } from "../../../../fields/Doc";
+import { InkData, InkField } from "../../../../fields/InkField";
+import { List } from "../../../../fields/List";
+import { RichTextField } from "../../../../fields/RichTextField";
+import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
import { Utils } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
@@ -65,58 +65,69 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
//make textbox and add it to this collection
// tslint:disable-next-line:prefer-const
let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
- if (e.key === ":") {
- DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument, x, y);
+ if (e.key === "?") {
+ ContextMenu.Instance.setDefaultItem("?", (str: string) => {
+ const textDoc = Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, {
+ _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 800, isAnnotating: false,
+ title: "bing", UseCors: true
+ });
+ this.props.addDocTab(textDoc, "onRight");
+ });
ContextMenu.Instance.displayMenu(this._downX, this._downY);
- } else if (e.key === "q" && e.ctrlKey) {
- e.preventDefault();
- (async () => {
- const text: string = await navigator.clipboard.readText();
- const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
- for (let i = 0; i < ns.length - 1; i++) {
- while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
- ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
- ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
- ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
- const sub = ns[i].endsWith("\r") ? 1 : 0;
- const br = ns[i + 1].trim() === "";
- ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
- if (br) break;
+ } else
+ if (e.key === ":") {
+ DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument, x, y);
+
+ ContextMenu.Instance.displayMenu(this._downX, this._downY);
+ } else if (e.key === "q" && e.ctrlKey) {
+ e.preventDefault();
+ (async () => {
+ const text: string = await navigator.clipboard.readText();
+ const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+ for (let i = 0; i < ns.length - 1; i++) {
+ while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
+ ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
+ ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
+ ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
+ const sub = ns[i].endsWith("\r") ? 1 : 0;
+ const br = ns[i + 1].trim() === "";
+ ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
+ if (br) break;
+ }
+ }
+ ns.map(line => {
+ const indent = line.search(/\S|$/);
+ const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: y, title: line });
+ this.props.addDocument(newBox);
+ y += 40 * this.props.getTransform().Scale;
+ });
+ })();
+ } else if (e.key === "b" && e.ctrlKey) {
+ e.preventDefault();
+ navigator.clipboard.readText().then(text => {
+ const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
+ if (ns.length === 1 && text.startsWith("http")) {
+ this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer
+ } else {
+ this.pasteTable(ns, x, y);
}
- }
- ns.map(line => {
- const indent = line.search(/\S|$/);
- const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: y, title: line });
- this.props.addDocument(newBox);
- y += 40 * this.props.getTransform().Scale;
});
- })();
- } else if (e.key === "b" && e.ctrlKey) {
- e.preventDefault();
- navigator.clipboard.readText().then(text => {
- const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
- if (ns.length === 1 && text.startsWith("http")) {
- this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer
- } else {
- this.pasteTable(ns, x, y);
+ } else if (!e.ctrlKey) {
+ FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout ? e.key : "";
+ const tbox = Docs.Create.TextDocument("", {
+ _width: 200, _height: 100, x: x, y: y, _autoHeight: true, _fontSize: NumCast(Doc.UserDoc().fontSize),
+ _fontFamily: StrCast(Doc.UserDoc().fontFamily), _backgroundColor: StrCast(Doc.UserDoc().backgroundColor),
+ title: "-typed text-"
+ });
+ const template = FormattedTextBox.DefaultLayout;
+ if (template instanceof Doc) {
+ tbox._width = NumCast(template._width);
+ tbox.layoutKey = "layout_" + StrCast(template.title);
+ Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
}
- });
- } else if (!e.ctrlKey) {
- FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout ? e.key : "";
- const tbox = Docs.Create.TextDocument("", {
- _width: 200, _height: 100, x: x, y: y, _autoHeight: true, _fontSize: NumCast(Doc.UserDoc().fontSize),
- _backgroundColor: StrCast(Doc.UserDoc().backgroundColor),
- title: "-typed text-"
- });
- const template = FormattedTextBox.DefaultLayout;
- if (template instanceof Doc) {
- tbox._width = NumCast(template._width);
- tbox.layoutKey = "layout_" + StrCast(template.title);
- Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
+ this.props.addLiveTextDocument(tbox);
}
- this.props.addLiveTextDocument(tbox);
- }
e.stopPropagation();
}
//heuristically converts pasted text into a table.
@@ -164,12 +175,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.clientX;
this._downY = this._lastY = e.clientY;
- if (e.button === 2 || (e.button === 0 && (e.altKey || MarqueeView.DragMarquee))) {
- if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
- this.setPreviewCursor(e.clientX, e.clientY, true);
- // (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
- e.preventDefault();
- }
+ // allow marquee if right click OR alt+left click OR space bar + left click
+ if (e.button === 2 || (e.button === 0 && (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))))) {
+ // if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
+ this.setPreviewCursor(e.clientX, e.clientY, true);
+ // (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
+ e.preventDefault();
+ // }
// bcz: do we need this? it kills the context menu on the main collection if !altKey
// e.stopPropagation();
}
@@ -322,9 +334,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
_LODdisable: true,
title: "a nested collection",
});
- // const dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
- // dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
- // this.marqueeInkDelete(inkData);
+ selected.forEach(d => d.context = newCollection);
this.hideMarquee();
return newCollection;
}
@@ -335,8 +345,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
SelectionManager.DeselectAll();
selected.forEach(d => this.props.removeDocument(d));
const newCollection = Doc.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
- this.props.addDocument(newCollection);
- this.props.selectDocuments([newCollection], []);
+ this.props.addDocument(newCollection!);
+ this.props.selectDocuments([newCollection!], []);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
}
@@ -347,10 +357,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const selected = this.marqueeSelect(false);
if (e instanceof KeyboardEvent ? e.key === "c" : true) {
selected.map(action(d => {
- //this.props.removeDocument(d);
- d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ const dx = NumCast(d.x);
+ const dy = NumCast(d.y);
+ delete d.x;
+ delete d.y;
+ delete d.activeFrame;
+ delete d.displayTimecode; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ d.x = dx - bounds.left - bounds.width / 2;
+ d.y = dy - bounds.top - bounds.height / 2;
return d;
}));
this.props.removeDocument(selected);
@@ -612,6 +626,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
render() {
return <div className="marqueeView"
style={{ overflow: StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
+ onDragOver={e => e.preventDefault()}
onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
{this._visible ? this.marqueeDiv : null}
{this.props.children}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index a09124304..c0e1a0232 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,10 +1,10 @@
import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from "react";
-import { Doc } from '../../../../new_fields/Doc';
-import { documentSchema } from '../../../../new_fields/documentSchemas';
-import { makeInterface } from '../../../../new_fields/Schema';
-import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../new_fields/Types';
+import { Doc } from '../../../../fields/Doc';
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { makeInterface } from '../../../../fields/Schema';
+import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../fields/Types';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -13,7 +13,7 @@ import { CollectionSubView } from '../CollectionSubView';
import "./collectionMulticolumnView.scss";
import ResizeBar from './MulticolumnResizer';
import WidthLabel from './MulticolumnWidthLabel';
-import { List } from '../../../../new_fields/List';
+import { List } from '../../../../fields/List';
import { returnZero, returnFalse, returnOne } from '../../../../Utils';
type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4326723b1..602246d07 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,10 +1,10 @@
import { observer } from 'mobx-react';
-import { makeInterface } from '../../../../new_fields/Schema';
-import { documentSchema } from '../../../../new_fields/documentSchemas';
+import { makeInterface } from '../../../../fields/Schema';
+import { documentSchema } from '../../../../fields/documentSchemas';
import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import * as React from "react";
-import { Doc } from '../../../../new_fields/Doc';
-import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types';
+import { Doc } from '../../../../fields/Doc';
+import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../fields/Types';
import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
import { Utils, returnZero, returnFalse, returnOne } from '../../../../Utils';
import "./collectionMultirowView.scss";
@@ -14,7 +14,7 @@ import HeightLabel from './MultirowHeightLabel';
import ResizeBar from './MultirowResizer';
import { undoBatch } from '../../../util/UndoManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
-import { List } from '../../../../new_fields/List';
+import { List } from '../../../../fields/List';
type MultirowDocument = makeInterface<[typeof documentSchema]>;
const MultirowDocument = makeInterface(documentSchema);
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index a24eb1631..734915a93 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,8 +1,8 @@
import * as React from "react";
import { observer } from "mobx-react";
import { observable, action } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast } from "../../../../fields/Types";
import { DimUnit } from "./CollectionMulticolumnView";
import { UndoManager } from "../../../util/UndoManager";
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
index 5b2054428..9985a9fba 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
@@ -1,8 +1,8 @@
import * as React from "react";
import { observer } from "mobx-react";
import { computed } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
import { EditableView } from "../../EditableView";
import { DimUnit } from "./CollectionMulticolumnView";
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
index 899577fd5..aa5439fa4 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -1,8 +1,8 @@
import * as React from "react";
import { observer } from "mobx-react";
import { computed } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
import { EditableView } from "../../EditableView";
import { DimUnit } from "./CollectionMultirowView";
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 5f00b18b9..d0bc4d01c 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,8 +1,8 @@
import * as React from "react";
import { observer } from "mobx-react";
import { observable, action } from "mobx";
-import { Doc } from "../../../../new_fields/Doc";
-import { NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc } from "../../../../fields/Doc";
+import { NumCast, StrCast } from "../../../../fields/Types";
import { DimUnit } from "./CollectionMultirowView";
import { UndoManager } from "../../../util/UndoManager";
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index b7f3dd995..13b9a2459 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -3,8 +3,8 @@ import { faArrowLeft, faCog, faEllipsisV, faExchangeAlt, faPlus, faTable, faTime
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
-import { StrCast } from "../../../new_fields/Types";
+import { Doc } from "../../../fields/Doc";
+import { StrCast } from "../../../fields/Types";
import { Utils } from "../../../Utils";
import { LinkManager } from "../../util/LinkManager";
import './LinkEditor.scss';
@@ -298,7 +298,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
<div className="linkEditor">
{this.props.hideback ? (null) : <button className="linkEditor-back" onPointerDown={() => this.props.showLinks()}><FontAwesomeIcon icon="arrow-left" size="sm" /></button>}
<div className="linkEditor-info">
- <p className="linkEditor-linkedTo">editing link to: <b>{destination.proto!.title}</b></p>
+ <p className="linkEditor-linkedTo">editing link to: <b>{destination.proto?.title ?? destination.title ?? "untitled"}</b></p>
<button className="linkEditor-button" onPointerDown={() => this.deleteLink()} title="Delete link"><FontAwesomeIcon icon="trash" size="sm" /></button>
</div>
{groups.length > 0 ? groups : <div className="linkEditor-group">There are currently no relationships associated with this link.</div>}
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index b768eacc3..786d6be47 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -4,7 +4,7 @@ import { DocumentView } from "../nodes/DocumentView";
import { LinkEditor } from "./LinkEditor";
import './LinkMenu.scss';
import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
+import { Doc } from "../../../fields/Doc";
import { LinkManager } from "../../util/LinkManager";
import { LinkMenuGroup } from "./LinkMenuGroup";
import { faTrash } from '@fortawesome/free-solid-svg-icons';
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index c97e1062d..89deb3a55 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -1,9 +1,9 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import { Doc } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { Docs } from "../../documents/Documents";
import { DragManager, SetupDrag } from "../../util/DragManager";
import { LinkManager } from "../../util/LinkManager";
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index b0c61cb04..17cd33241 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -3,8 +3,8 @@ import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
-import { Doc, DocListCast } from '../../../new_fields/Doc';
-import { Cast, StrCast } from '../../../new_fields/Types';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Cast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { LinkManager } from '../../util/LinkManager';
import { ContextMenu } from '../ContextMenu';
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1c5e13620..1a935d9b0 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -2,23 +2,23 @@ import React = require("react");
import { FieldViewProps, FieldView } from './FieldView';
import { observer } from "mobx-react";
import "./AudioBox.scss";
-import { Cast, DateCast, NumCast } from "../../../new_fields/Types";
-import { AudioField, nullAudio } from "../../../new_fields/URLField";
+import { Cast, DateCast, NumCast } from "../../../fields/Types";
+import { AudioField, nullAudio } from "../../../fields/URLField";
import { ViewBoxBaseComponent } from "../DocComponent";
-import { makeInterface, createSchema } from "../../../new_fields/Schema";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { makeInterface, createSchema } from "../../../fields/Schema";
+import { documentSchema } from "../../../fields/documentSchemas";
import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse, returnZero } from "../../../Utils";
import { runInAction, observable, reaction, IReactionDisposer, computed, action } from "mobx";
-import { DateField } from "../../../new_fields/DateField";
+import { DateField } from "../../../fields/DateField";
import { SelectionManager } from "../../util/SelectionManager";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc, DocListCast } from "../../../fields/Doc";
import { ContextMenuProps } from "../ContextMenuItem";
import { ContextMenu } from "../ContextMenu";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { DocumentView } from "./DocumentView";
import { Docs, DocUtils } from "../../documents/Documents";
-import { ComputedField } from "../../../new_fields/ScriptField";
+import { ComputedField } from "../../../fields/ScriptField";
import { Networking } from "../../Network";
import { LinkAnchorBox } from "./LinkAnchorBox";
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index cdbe506a5..910aa744d 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,18 +1,22 @@
import { computed, IReactionDisposer, observable, reaction, trace } from "mobx";
import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { Transform } from "../../util/Transform";
import { DocComponent } from "../DocComponent";
import "./CollectionFreeFormDocumentView.scss";
import { DocumentView, DocumentViewProps } from "./DocumentView";
import React = require("react");
-import { Document } from "../../../new_fields/documentSchemas";
-import { TraceMobx } from "../../../new_fields/util";
+import { Document } from "../../../fields/documentSchemas";
+import { TraceMobx } from "../../../fields/util";
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
+import { List } from "../../../fields/List";
+import { numberRange } from "../../../Utils";
+import { ComputedField } from "../../../fields/ScriptField";
+import { listSpec } from "../../../fields/Schema";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
- dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, highlight?: boolean, z: number, transition?: string } | undefined;
+ dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
zIndex?: number;
highlight?: boolean;
@@ -31,10 +35,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
const rnd = seed / 233280;
return min + rnd * (max - min);
}
- get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
+ get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
get X() { return this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
get Y() { return this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
+ get Opacity() { return this.dataProvider ? this.dataProvider.opacity : Cast(this.layoutDoc.opacity, "number", null); }
get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
get Highlight() { return this.dataProvider?.highlight; }
get width() { return this.props.sizeProvider && this.sizeProvider ? this.sizeProvider.width : this.layoutDoc[WidthSym](); }
@@ -62,6 +67,62 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
return undefined;
}
+
+ public static getValues(doc: Doc, time: number) {
+ const timecode = Math.round(time);
+ return ({
+ x: Cast(doc["x-indexed"], listSpec("number"), []).reduce((p, x, i) => (i <= timecode && x !== undefined) || p === undefined ? x : p, undefined as any as number),
+ y: Cast(doc["y-indexed"], listSpec("number"), []).reduce((p, y, i) => (i <= timecode && y !== undefined) || p === undefined ? y : p, undefined as any as number),
+ opacity: Cast(doc["opacity-indexed"], listSpec("number"), []).reduce((p, o, i) => i <= timecode || p === undefined ? o : p, undefined as any as number),
+ });
+ }
+
+ public static setValues(time: number, d: Doc, x?: number, y?: number, opacity?: number) {
+ const timecode = Math.round(time);
+ Cast(d["x-indexed"], listSpec("number"), [])[Math.max(0, timecode - 1)] = x as any as number;
+ Cast(d["y-indexed"], listSpec("number"), [])[Math.max(0, timecode - 1)] = y as any as number;
+ Cast(d["x-indexed"], listSpec("number"), [])[timecode] = x as any as number;
+ Cast(d["y-indexed"], listSpec("number"), [])[timecode] = y as any as number;
+ Cast(d["opacity-indexed"], listSpec("number"), null)[timecode] = opacity as any as number;
+ }
+ public static updateKeyframe(docs: Doc[], time: number) {
+ const timecode = Math.round(time);
+ docs.forEach(doc => {
+ const xindexed = Cast(doc['x-indexed'], listSpec("number"), null);
+ const yindexed = Cast(doc['y-indexed'], listSpec("number"), null);
+ const opacityindexed = Cast(doc['opacity-indexed'], listSpec("number"), null);
+ xindexed?.length <= timecode + 1 && xindexed.push(undefined as any as number);
+ yindexed?.length <= timecode + 1 && yindexed.push(undefined as any as number);
+ opacityindexed?.length <= timecode + 1 && opacityindexed.push(undefined as any as number);
+ doc.transition = "all 1s";
+ });
+ setTimeout(() => docs.forEach(doc => doc.transition = "inherit"), 1010);
+ }
+
+ public static gotoKeyframe(docs: Doc[]) {
+ docs.forEach(doc => doc.transition = "all 1s");
+ setTimeout(() => docs.forEach(doc => doc.transition = "inherit"), 1010);
+ }
+
+ public static setupKeyframes(docs: Doc[], timecode: number, progressivize: boolean = false) {
+ docs.forEach((doc, i) => {
+ const curTimecode = progressivize ? i : timecode;
+ const xlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
+ const ylist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
+ const olist = new List<number>(numberRange(timecode + 1).map(t => progressivize && t < i ? 0 : 1));
+ xlist[Math.max(curTimecode - 1, 0)] = xlist[curTimecode] = NumCast(doc.x);
+ ylist[Math.max(curTimecode - 1, 0)] = ylist[curTimecode] = NumCast(doc.y);
+ doc["x-indexed"] = xlist;
+ doc["y-indexed"] = ylist;
+ doc["opacity-indexed"] = olist;
+ doc.activeFrame = ComputedField.MakeFunction("self.context ? (self.context.currentFrame||0) : 0");
+ doc.x = ComputedField.MakeInterpolated("x", "activeFrame");
+ doc.y = ComputedField.MakeInterpolated("y", "activeFrame");
+ doc.opacity = ComputedField.MakeInterpolated("opacity", "activeFrame");
+ doc.transition = "inherit";
+ });
+ }
+
nudge = (x: number, y: number) => {
this.props.Document.x = NumCast(this.props.Document.x) + x;
this.props.Document.y = NumCast(this.props.Document.y) + y;
@@ -72,14 +133,16 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
panelHeight = () => (this.sizeProvider?.height || this.props.PanelHeight?.());
getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y).scale(1 / this.contentScaling());
focusDoc = (doc: Doc) => this.props.focus(doc, false);
+ opacity = () => this.Opacity;
NativeWidth = () => this.nativeWidth;
NativeHeight = () => this.nativeHeight;
render() {
TraceMobx();
+ const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
return <div className="collectionFreeFormDocumentView-container"
style={{
boxShadow:
- this.layoutDoc.opacity === 0 ? undefined : // if it's not visible, then no shadow
+ this.Opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
this.props.backgroundHalo?.() ? (`${this.props.backgroundColor?.(this.props.Document)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
this.layoutDoc.isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
@@ -91,9 +154,17 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
width: this.width,
height: this.height,
zIndex: this.ZInd,
+ mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
display: this.ZInd === -99 ? "none" : undefined,
- pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents ? "all" : undefined
+ pointerEvents: this.props.Document.isBackground || this.Opacity === 0 ? "none" : this.props.pointerEvents ? "all" : undefined
}} >
+ {Doc.UserDoc().renderStyle !== "comic" ? (null) :
+ <div style={{ width: "100%", height: "100%", position: "absolute" }}>
+ <svg style={{ transform: `scale(1,${this.props.PanelHeight() / this.props.PanelWidth()})`, transformOrigin: "top left", overflow: "visible" }} viewBox="0 0 12 14">
+ <path d="M 7 0 C 9 -1 13 1 12 4 C 11 10 13 12 10 12 C 6 12 7 13 2 12 Q -1 11 0 8 C 1 4 0 4 0 2 C 0 0 1 0 1 0 C 3 0 3 1 7 0"
+ style={{ stroke: "black", fill: backgroundColor, strokeWidth: 0.2 }} />
+ </svg>
+ </div>}
{!this.props.fitToBox ?
<DocumentView {...this.props}
@@ -102,6 +173,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
backgroundColor={this.props.backgroundColor}
+ opacity={this.opacity}
NativeHeight={this.NativeHeight}
NativeWidth={this.NativeWidth}
PanelWidth={this.panelWidth}
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 7ab6d99c2..6d53915ea 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -1,10 +1,10 @@
import React = require("react");
import { observer } from "mobx-react";
import { SketchPicker } from 'react-color';
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { StrCast } from "../../../new_fields/Types";
-import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface } from "../../../fields/Schema";
+import { StrCast } from "../../../fields/Types";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { SelectionManager } from "../../util/SelectionManager";
import { ViewBoxBaseComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
@@ -27,6 +27,10 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
<SketchPicker onChange={InkingControl.Instance.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
color={StrCast(CurrentUserUtils.ActivePen ? CurrentUserUtils.ActivePen.backgroundColor : undefined,
StrCast(selDoc?._backgroundColor, StrCast(selDoc?.backgroundColor, "black")))} />
+ <div style={{ display: "grid", gridTemplateColumns: "20% 80%", paddingTop: "10px" }}>
+ <div>{InkingControl.Instance.selectedWidth ?? 2}</div>
+ <input type="range" value={InkingControl.Instance.selectedWidth ?? 2} defaultValue={2} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => InkingControl.Instance.switchWidth(e.target.value)} />
+ </div>
</div>;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
new file mode 100644
index 000000000..810a824cf
--- /dev/null
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -0,0 +1,93 @@
+.comparisonBox-interactive,
+.comparisonBox {
+ border-radius: inherit;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ z-index: 0;
+ pointer-events: none;
+
+ .clip-div {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ overflow: hidden;
+
+ .beforeBox-cont {
+ height: 100%;
+ overflow: hidden;
+ }
+ }
+
+ .slide-bar {
+ position: absolute;
+ height: 100%;
+ width: 3px;
+ display: inline-block;
+ background: white;
+
+ .slide-handle {
+ position: absolute;
+ display: none;
+ height: 20px;
+ width: 30px;
+ bottom: 0px;
+ left: -10.5px;
+
+ .left-handle,
+ .right-handle {
+ width: 15px;
+ }
+ }
+ }
+
+ .afterBox-cont {
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 100%;
+ width: 100%;
+ overflow: hidden;
+ }
+
+ .clear-button {
+ position: absolute;
+ top: 3px;
+ opacity: 0.8;
+ pointer-events: all;
+ cursor: pointer;
+ }
+
+ .clear-button.before {
+ left: 3px;
+ }
+
+ .clear-button.after {
+ right: 3px;
+ }
+
+ .placeholder {
+ width: 50%;
+ height: 50%;
+ margin-top: 25%;
+ margin-left: 25%;
+
+ .upload-icon {
+ width: 100%;
+ height: 100%;
+ opacity: 0.5;
+ }
+ }
+}
+
+.comparisonBox-interactive {
+ pointer-events: unset;
+ cursor: ew-resize;
+
+ .slide-bar {
+ .slide-handle {
+ display: flex;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
new file mode 100644
index 000000000..cce60628d
--- /dev/null
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -0,0 +1,114 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
+import { observer } from "mobx-react";
+import { Doc } from '../../../fields/Doc';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { createSchema, makeInterface } from '../../../fields/Schema';
+import { NumCast, Cast, StrCast } from '../../../fields/Types';
+import { DragManager } from '../../util/DragManager';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
+import { FieldView, FieldViewProps } from './FieldView';
+import "./ComparisonBox.scss";
+import React = require("react");
+import { ContentFittingDocumentView } from './ContentFittingDocumentView';
+import { undoBatch } from '../../util/UndoManager';
+import { setupMoveUpEvents, emptyFunction } from '../../../Utils';
+import { SnappingManager } from '../../util/SnappingManager';
+import { DocumentViewProps } from './DocumentView';
+
+export const comparisonSchema = createSchema({});
+
+type ComparisonDocument = makeInterface<[typeof comparisonSchema, typeof documentSchema]>;
+const ComparisonDocument = makeInterface(comparisonSchema, documentSchema);
+
+@observer
+export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, ComparisonDocument>(ComparisonDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); }
+ protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
+ private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
+
+ @observable _animating = "";
+
+ protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
+ this._disposers[disposerId]?.();
+ if (ele) {
+ // create disposers identified by disposerId to remove drag & drop listeners
+ this._disposers[disposerId] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.dropHandler(e, dropEvent, fieldKey), this.layoutDoc);
+ }
+ }
+
+ @undoBatch
+ private dropHandler = (event: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
+ event.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
+ const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
+ if (droppedDocs?.length) {
+ this.dataDoc[fieldKey] = droppedDocs[0];
+ }
+ }
+
+ private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => {
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, action(() => {
+ // on click, animate slider movement to the targetWidth
+ this._animating = "all 1s";
+ this.layoutDoc._clipWidth = targetWidth * 100 / this.props.PanelWidth();
+ setTimeout(action(() => this._animating = ""), 1000);
+ }), false);
+ }
+
+ @action
+ private onPointerMove = ({ movementX }: PointerEvent) => {
+ const width = movementX * this.props.ScreenToLocalTransform().Scale + NumCast(this.layoutDoc._clipWidth) / 100 * this.props.PanelWidth();
+ if (width && width > 5 && width < this.props.PanelWidth()) {
+ this.layoutDoc._clipWidth = width * 100 / this.props.PanelWidth();
+ }
+ return false;
+ }
+
+ @undoBatch
+ clearDoc = (e: React.MouseEvent, fieldKey: string) => {
+ e.stopPropagation; // prevent click event action (slider movement) in registerSliding
+ delete this.dataDoc[fieldKey];
+ }
+
+ render() {
+ const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%";
+ const childProps: DocumentViewProps = { ...this.props, pointerEvents: false, parentActive: this.props.active };
+ const clearButton = (which: string) => {
+ return <div className={`clear-button ${which}`}
+ onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding
+ onClick={e => this.clearDoc(e, `${which}Doc`)}>
+ <FontAwesomeIcon className={`clear-button ${which}`} icon={"times"} size="sm" />
+ </div>;
+ };
+ const displayDoc = (which: string) => {
+ const whichDoc = Cast(this.dataDoc[`${which}Doc`], Doc, null);
+ return whichDoc ? <>
+ <ContentFittingDocumentView {...childProps} Document={whichDoc} />
+ {clearButton(which)}
+ </> : // placeholder image if doc is missing
+ <div className="placeholder">
+ <FontAwesomeIcon className="upload-icon" icon={"cloud-upload-alt"} size="lg" />
+ </div>;
+ };
+ const displayBox = (which: string, index: number, cover: number) => {
+ return <div className={`${which}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }}
+ onPointerDown={e => this.registerSliding(e, cover)}
+ ref={ele => this.createDropTarget(ele, `${which}Doc`, index)} >
+ {displayDoc(which)}
+ </div>;
+ };
+
+ return (
+ <div className={`comparisonBox${this.active() || SnappingManager.GetIsDragging() ? "-interactive" : ""}` /* change className to easily disable/enable pointer events in CSS */}>
+ {displayBox("after", 1, this.props.PanelWidth() - 5)}
+ <div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, "gray") }}>
+ {displayBox("before", 0, 5)}
+ </div>
+
+ <div className="slide-bar" style={{ left: `calc(${clipWidth} - 0.5px)` }}
+ onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */ >
+ <div className="slide-handle" />
+ </div>
+ </div >);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 1c6250b94..a90b4668e 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -2,9 +2,9 @@ import React = require("react");
import { computed } from "mobx";
import { observer } from "mobx-react";
import "react-table/react-table.css";
-import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
-import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, Opt, WidthSym, HeightSym } from "../../../fields/Doc";
+import { NumCast, StrCast, Cast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
import { emptyFunction, returnOne } from "../../../Utils";
import '../DocumentDecorations.scss';
import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index af8dc704c..0c5239d66 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -1,12 +1,12 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Field } from "../../../new_fields/Doc";
-import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface, listSpec } from "../../../new_fields/Schema";
-import { ComputedField } from "../../../new_fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, Field } from "../../../fields/Doc";
+import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface, listSpec } from "../../../fields/Schema";
+import { ComputedField } from "../../../fields/ScriptField";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { DragManager } from "../../util/DragManager";
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 729209fd7..03383882d 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,7 +1,7 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Opt, Field } from "../../../new_fields/Doc";
-import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { Doc, Opt, Field, AclSym, AclPrivate } from "../../../fields/Doc";
+import { Cast, StrCast, NumCast } from "../../../fields/Types";
import { OmitKeys, Without, emptyPath } from "../../../Utils";
import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -31,13 +31,14 @@ import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
import { LinkAnchorBox } from "./LinkAnchorBox";
import { PresElementBox } from "../presentationview/PresElementBox";
import { ScreenshotBox } from "./ScreenshotBox";
+import { ComparisonBox } from "./ComparisonBox";
import { VideoBox } from "./VideoBox";
import { WebBox } from "./WebBox";
import { InkingStroke } from "../InkingStroke";
import React = require("react");
import { RecommendationsBox } from "../RecommendationsBox";
-import { TraceMobx } from "../../../new_fields/util";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { TraceMobx } from "../../../fields/util";
+import { ScriptField } from "../../../fields/ScriptField";
import XRegExp = require("xregexp");
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -185,7 +186,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
const bindings = this.CreateBindings(onClick, onInput);
// layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
- return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
+ return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || this.layoutDoc[AclSym] === AclPrivate) ? (null) :
<ObserverJsxParser
key={42}
blacklistedAttrs={[]}
@@ -195,7 +196,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, SearchItem,
ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
- RecommendationsBox, ScreenshotBox, HTMLtag
+ RecommendationsBox, ScreenshotBox, HTMLtag, ComparisonBox
}}
bindings={bindings}
jsx={layoutFrame}
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index f56f5e829..fb54f18e8 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -3,7 +3,7 @@ import * as React from "react";
import { DocumentView } from "./DocumentView";
import { DocumentManager } from "../../util/DocumentManager";
import { Transformer, Scripting, ts } from "../../util/Scripting";
-import { Field } from "../../../new_fields/Doc";
+import { Field } from "../../../fields/Doc";
@observer
export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> {
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index dea09cb30..b7726f7ba 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -94,6 +94,7 @@
text-align: center;
text-overflow: ellipsis;
white-space: pre;
+ position: absolute;
}
.documentView-titleWrapper-hover {
display:none;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3d20148e7..b4757e0a4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -4,16 +4,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym } from "../../../new_fields/Doc";
-import { Document } from '../../../new_fields/documentSchemas';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { InkTool } from '../../../new_fields/InkField';
-import { listSpec } from "../../../new_fields/Schema";
-import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { ImageField } from '../../../new_fields/URLField';
-import { TraceMobx } from '../../../new_fields/util';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclSym, AclReadonly, AclPrivate } from "../../../fields/Doc";
+import { Document } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { listSpec } from "../../../fields/Schema";
+import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+import { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
@@ -45,6 +44,7 @@ import "./DocumentView.scss";
import { LinkAnchorBox } from './LinkAnchorBox';
import { RadialMenu } from './RadialMenu';
import React = require("react");
+import { undo } from 'prosemirror-history';
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -92,6 +92,7 @@ export interface DocumentViewProps {
pinToPres: (document: Doc) => void;
backgroundHalo?: () => boolean;
backgroundColor?: (doc: Doc) => string | undefined;
+ opacity?: () => number | undefined;
ChromeHeight?: () => number;
dontRegisterView?: boolean;
layoutKey?: string;
@@ -684,8 +685,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
- toggleLockTransform = (): void => {
- this.Document.lockedTransform = this.Document.lockedTransform ? undefined : true;
+ setAcl = (acl: "readOnly" | "addOnly" | "ownerOnly") => {
+ this.layoutDoc.ACL = this.dataDoc.ACL = acl;
+ DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => {
+ if (d.author === Doc.CurrentUserEmail) d.ACL = acl;
+ const data = d[DataSym];
+ if (data && data.author === Doc.CurrentUserEmail) data.ACL = acl;
+ });
}
@action
@@ -748,14 +754,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const more = cm.findByDescription("More...");
const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
+ moreItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" });
+ moreItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" });
+ moreItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" });
moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
- moreItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
if (!ClientUtils.RELEASE) {
// let copies: ContextMenuProps[] = [];
- moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
+ moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
// cm.addItem({ description: "Copy...", subitems: copies, icon: "copy" });
}
if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
@@ -764,10 +772,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
}
moreItems.push({
- description: "Download document", icon: "download", event: async () =>
- console.log(JSON.parse(await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
+ description: "Download document", icon: "download", event: async () => {
+ const response = await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' }
- })))
+ });
+ console.log(response ? JSON.parse(response) : undefined);
+ }
// const a = document.createElement("a");
// const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
// a.href = url;
@@ -970,9 +980,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
}
childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
- panelWidth = () => this.props.PanelWidth();
- panelHeight = () => this.props.PanelHeight();
- screenToLocalTransform = () => this.props.ScreenToLocalTransform();
@computed get contents() {
TraceMobx();
return (<>
@@ -993,10 +1000,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
addDocument={this.props.addDocument}
removeDocument={this.props.removeDocument}
moveDocument={this.props.moveDocument}
- ScreenToLocalTransform={this.screenToLocalTransform}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
ignoreAutoHeight={this.props.ignoreAutoHeight}
focus={this.props.focus}
parentActive={this.props.parentActive}
@@ -1116,9 +1123,22 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}), 400);
});
+ renderLock() {
+ return (this.Document.isBackground !== undefined || this.isSelected(false)) &&
+ ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) &&
+ this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ?
+ <div className="documentView-lock" onClick={() => this.toggleBackground(true)}>
+ <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" />
+ </div>
+ : (null);
+ }
+
render() {
+ if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
if (!(this.props.Document instanceof Doc)) return (null);
- const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
+ const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
+ const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
+ const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
const borderRounding = this.layoutDoc.borderRounding;
@@ -1155,7 +1175,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
border: this.layoutDoc.border ? StrCast(this.layoutDoc.border) : highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined,
background: finalColor,
- opacity: this.Document.opacity,
+ opacity: finalOpacity,
fontFamily: StrCast(this.Document._fontFamily, "inherit"),
fontSize: Cast(this.Document._fontSize, "number", null)
}}>
@@ -1164,9 +1184,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
<div className="documentView-contentBlocker" />
</> :
this.innards}
- {(this.Document.isBackground !== undefined || this.isSelected(false)) && this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ?
- <div className="documentView-lock" onClick={() => this.toggleBackground(true)}> <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} size="lg" /> </div>
- : (null)}
+ {this.renderLock()}
</div>;
{ this._showKPQuery ? <KeyphraseQueryView keyphrases={this._queries}></KeyphraseQueryView> : undefined; }
}
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 3c7f1f206..92ca276cb 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -1,8 +1,8 @@
import React = require("react");
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { Cast, NumCast } from "../../../fields/Types";
import { observer } from "mobx-react";
-import { Id } from "../../../new_fields/FieldSymbols";
+import { Id } from "../../../fields/FieldSymbols";
import FaceRectangle from "./FaceRectangle";
interface FaceRectanglesProps {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 9fba91fd8..cbb1b5b00 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,11 +1,11 @@
import React = require("react");
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { DateField } from "../../../new_fields/DateField";
-import { Doc, FieldResult, Opt, Field } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { AudioField, VideoField } from "../../../new_fields/URLField";
+import { DateField } from "../../../fields/DateField";
+import { Doc, FieldResult, Opt, Field } from "../../../fields/Doc";
+import { List } from "../../../fields/List";
+import { ScriptField } from "../../../fields/ScriptField";
+import { AudioField, VideoField } from "../../../fields/URLField";
import { Transform } from "../../util/Transform";
import { CollectionView } from "../collections/CollectionView";
import { AudioBox } from "./AudioBox";
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 0d597b3a8..cf0b16c7c 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -1,16 +1,16 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
+import { createSchema, makeInterface } from '../../../fields/Schema';
import { DocComponent } from '../DocComponent';
import './FontIconBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
-import { StrCast, Cast } from '../../../new_fields/Types';
+import { StrCast, Cast } from '../../../fields/Types';
import { Utils } from "../../../Utils";
import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
-import { Doc } from '../../../new_fields/Doc';
+import { Doc } from '../../../fields/Doc';
import { ContextMenu } from '../ContextMenu';
-import { ScriptField } from '../../../new_fields/ScriptField';
+import { ScriptField } from '../../../fields/ScriptField';
const FontIconSchema = createSchema({
icon: "string"
});
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index aaebceaa2..6913dfbc7 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -4,22 +4,21 @@ import { faAsterisk, faBrain, faFileAudio, faImage, faPaintBrush } from '@fortaw
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { ObjectField } from '../../../new_fields/ObjectField';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { ComputedField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
-import { AudioField, ImageField } from '../../../new_fields/URLField';
-import { TraceMobx } from '../../../new_fields/util';
+import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { ComputedField } from '../../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { AudioField, ImageField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
import { emptyFunction, returnOne, Utils, returnZero } from '../../../Utils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs } from '../../documents/Documents';
import { Networking } from '../../Network';
import { DragManager } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from "../../views/ContextMenu";
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -267,7 +266,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
if (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc) {
requestImageSize(imgPath).then(action((inquiredSize: any) => {
const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180;
- const rotatedNativeSize = rotation === 90 || rotation === 270 ? { height: inquiredSize.width, width: inquiredSize.height } : inquiredSize;
+ const rotatedNativeSize = { width: inquiredSize.width, height: inquiredSize.height };
+ if (inquiredSize.orientation === 6 || rotation === 90 || rotation === 270) {
+ rotatedNativeSize.width = inquiredSize.height;
+ rotatedNativeSize.height = inquiredSize.width;
+ }
const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width;
if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) {
this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 39d7109b1..e983852ea 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -1,13 +1,13 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Field, FieldResult } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { RichTextField } from "../../../new_fields/RichTextField";
-import { listSpec } from "../../../new_fields/Schema";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
-import { ImageField } from "../../../new_fields/URLField";
+import { Doc, Field, FieldResult } from "../../../fields/Doc";
+import { List } from "../../../fields/List";
+import { RichTextField } from "../../../fields/RichTextField";
+import { listSpec } from "../../../fields/Schema";
+import { ComputedField, ScriptField } from "../../../fields/ScriptField";
+import { Cast, FieldValue, NumCast } from "../../../fields/Types";
+import { ImageField } from "../../../fields/URLField";
import { Docs } from "../../documents/Documents";
import { SetupDrag } from "../../util/DragManager";
import { CompiledScript, CompileScript, ScriptOptions } from "../../util/Scripting";
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 6dc4ae578..3cbe3e494 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,6 +1,6 @@
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
-import { Doc, Field, Opt } from '../../../new_fields/Doc';
+import { Doc, Field, Opt } from '../../../fields/Doc';
import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
@@ -99,9 +99,9 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
<div className="keyValuePair-td-key-container">
<button style={hover} className="keyValuePair-td-key-delete" onClick={undoBatch(() => {
if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
- props.Document[props.fieldKey] = undefined;
+ delete props.Document[props.fieldKey];
}
- else props.Document.proto![props.fieldKey] = undefined;
+ else delete props.Document.proto![props.fieldKey];
})}>
X
</button>
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 38558c43d..a0946e3a6 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -3,11 +3,11 @@ import { faEdit } from '@fortawesome/free-regular-svg-icons';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast } from '../../../new_fields/Doc';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { List } from '../../../new_fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { List } from '../../../fields/List';
+import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
@@ -16,9 +16,6 @@ import { ViewBoxBaseComponent } from '../DocComponent';
import { FieldView, FieldViewProps } from './FieldView';
import './LabelBox.scss';
-
-library.add(faEdit as any);
-
const LabelSchema = createSchema({});
type LabelDocument = makeInterface<[typeof LabelSchema, typeof documentSchema]>;
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index bc36e056e..83245a89c 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,9 +1,9 @@
import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { Utils, setupMoveUpEvents, emptyFunction } from '../../../Utils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
@@ -16,7 +16,7 @@ import { ContextMenu } from "../ContextMenu";
import { LinkEditor } from "../linking/LinkEditor";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SelectionManager } from "../../util/SelectionManager";
-import { TraceMobx } from "../../../new_fields/util";
+import { TraceMobx } from "../../../fields/util";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -72,7 +72,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
this._timeout = setTimeout(action(() => {
- DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "inTab")), false);
+ DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, e.altKey ? "inTab" : "onRight")), false);
this._editing = false;
}), 300 - (Date.now() - this._lastTap));
}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 740f2ef04..3f942e87b 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,13 +1,13 @@
import React = require("react");
import { observer } from "mobx-react";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface, listSpec } from "../../../new_fields/Schema";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface, listSpec } from "../../../fields/Schema";
import { returnFalse, returnZero } from "../../../Utils";
import { CollectionTreeView } from "../collections/CollectionTreeView";
import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from './FieldView';
import "./LinkBox.scss";
-import { Cast } from "../../../new_fields/Types";
+import { Cast } from "../../../fields/Types";
type LinkDocument = makeInterface<[typeof documentSchema]>;
const LinkDocument = makeInterface(documentSchema);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3712c648e..493f23dc4 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,11 +3,11 @@ import { action, observable, runInAction, reaction, IReactionDisposer, trace, un
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
-import { Opt, WidthSym, Doc, HeightSym } from "../../../new_fields/Doc";
-import { makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { PdfField, URLField } from "../../../new_fields/URLField";
+import { Opt, WidthSym, Doc, HeightSym } from "../../../fields/Doc";
+import { makeInterface } from "../../../fields/Schema";
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { PdfField, URLField } from "../../../fields/URLField";
import { Utils } from '../../../Utils';
import { undoBatch } from '../../util/UndoManager';
import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -20,7 +20,7 @@ import { pageSchema } from "./ImageBox";
import { KeyCodes } from '../../util/KeyCodes';
import "./PDFBox.scss";
import React = require("react");
-import { documentSchema } from '../../../new_fields/documentSchemas';
+import { documentSchema } from '../../../fields/documentSchemas';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index e203c7efa..9f1e99c77 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1,12 +1,12 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, DocCastAsync } from "../../../new_fields/Doc";
-import { InkTool } from "../../../new_fields/InkField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { returnFalse } from "../../../Utils";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { Doc, DocListCast, DocCastAsync } from "../../../fields/Doc";
+import { InkTool } from "../../../fields/InkField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
+import { returnFalse, returnOne } from "../../../Utils";
+import { documentSchema } from "../../../fields/documentSchemas";
import { DocumentManager } from "../../util/DocumentManager";
import { undoBatch } from "../../util/UndoManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -15,11 +15,10 @@ import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
import "./PresBox.scss";
import { ViewBoxBaseComponent } from "../DocComponent";
-import { makeInterface } from "../../../new_fields/Schema";
-import { List } from "../../../new_fields/List";
+import { makeInterface } from "../../../fields/Schema";
import { Docs } from "../../documents/Documents";
-import { PrefetchProxy } from "../../../new_fields/Proxy";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { PrefetchProxy } from "../../../fields/Proxy";
+import { ScriptField } from "../../../fields/ScriptField";
import { Scripting } from "../../util/Scripting";
type PresBoxSchema = makeInterface<[typeof documentSchema]>;
@@ -59,7 +58,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
next = () => {
this.updateCurrentPresentation();
- if (this.childDocs[this.itemIndex + 1] !== undefined) {
+ const presTargetDoc = Cast(this.childDocs[this.itemIndex].presentationTargetDoc, Doc, null);
+ const lastFrame = Cast(presTargetDoc.lastFrame, "number", null);
+ const curFrame = NumCast(presTargetDoc.currentFrame);
+ if (lastFrame !== undefined && curFrame < lastFrame) {
+ presTargetDoc.transition = "all 1s";
+ setTimeout(() => presTargetDoc.transition = undefined, 1010);
+ presTargetDoc.currentFrame = curFrame + 1;
+ }
+ else if (this.childDocs[this.itemIndex + 1] !== undefined) {
let nextSelected = this.itemIndex + 1;
this.gotoDocument(nextSelected, this.itemIndex);
@@ -188,11 +195,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//The function that is called when a document is clicked or reached through next or back.
//it'll also execute the necessary actions if presentation is playing.
- public gotoDocument = (index: number, fromDoc: number) => {
+ public gotoDocument = action((index: number, fromDoc: number) => {
this.updateCurrentPresentation();
Doc.UnBrushAllDocs();
if (index >= 0 && index < this.childDocs.length) {
this.rootDoc._itemIndex = index;
+ const presTargetDoc = Cast(this.childDocs[this.itemIndex].presentationTargetDoc, Doc, null);
+ if (presTargetDoc.lastFrame !== undefined) {
+ presTargetDoc.currentFrame = 0;
+ }
if (!this.layoutDoc.presStatus) {
this.layoutDoc.presStatus = true;
@@ -203,7 +214,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.hideIfNotPresented(index);
this.showAfterPresented(index);
}
- }
+ });
//The function that starts or resets presentaton functionally, depending on status flag.
startOrResetPres = () => {
@@ -286,7 +297,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
(this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
render() {
- this.rootDoc.presOrderedDocs = new List<Doc>(this.childDocs.map((child, i) => child));
+ // console.log("render = " + this.layoutDoc.title + " " + this.layoutDoc.presStatus);
+ // const presOrderedDocs = DocListCast(this.rootDoc.presOrderedDocs);
+ // if (presOrderedDocs.length != this.childDocs.length || presOrderedDocs.some((pd, i) => pd !== this.childDocs[i])) {
+ // this.rootDoc.presOrderedDocs = new List<Doc>(this.childDocs.slice());
+ // }
+ this.childDocs.slice(); // needed to insure that the childDocs are loaded for looking up fields
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
<div className="presBox-buttons" style={{ display: this.rootDoc._chromeStatus === "disabled" ? "none" : undefined }}>
@@ -316,9 +332,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
PanelWidth={this.props.PanelWidth}
PanelHeight={this.panelHeight}
moveDocument={returnFalse}
+ childOpacity={returnOne}
childLayoutTemplate={this.childLayoutTemplate}
filterAddDocument={this.addDocumentFilter}
removeDocument={returnFalse}
+ dontRegisterView={true}
focus={this.selectElement}
ScreenToLocalTransform={this.getTransform} />
: (null)
@@ -332,5 +350,6 @@ Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, d
if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 50 : 46;
if (field === 'presStatus') return container.presStatus;
if (field === '_itemIndex') return container._itemIndex;
+ if (field === 'presBox') return container;
return undefined;
});
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index a0ecc9ff5..29e3c008a 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -5,10 +5,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from 'request-promise';
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast } from "../../../new_fields/Types";
-import { VideoField } from "../../../new_fields/URLField";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface, listSpec } from "../../../fields/Schema";
+import { Cast, NumCast } from "../../../fields/Types";
+import { VideoField } from "../../../fields/URLField";
import { emptyFunction, returnFalse, returnOne, Utils, returnZero } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -18,6 +18,8 @@ import { ViewBoxBaseComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
import "./ScreenshotBox.scss";
+import { Doc, WidthSym, HeightSym } from "../../../fields/Doc";
+import { OverlayView } from "../OverlayView";
const path = require('path');
type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
@@ -72,7 +74,14 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y),
_width: 150, _height: height / width * 150, title: "--screenshot--"
});
- this.props.addDocument?.(imageSummary);
+ if (!this.props.addDocument || this.props.addDocument === returnFalse) {
+ const spt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ imageSummary.x = spt[0];
+ imageSummary.y = spt[1];
+ Cast(Cast(Doc.UserDoc().myOverlayDocuments, Doc, null)?.data, listSpec(Doc), []).push(imageSummary);
+ } else {
+ this.props.addDocument?.(imageSummary);
+ }
}
}, 500);
});
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index c607d6614..0944edf60 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,10 +1,10 @@
import { action, observable, computed } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { StrCast, ScriptCast, Cast } from "../../../new_fields/Types";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { createSchema, makeInterface, listSpec } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
+import { StrCast, ScriptCast, Cast } from "../../../fields/Types";
import { InteractionUtils } from "../../util/InteractionUtils";
import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting";
import { ViewBoxAnnotatableComponent } from "../DocComponent";
@@ -13,7 +13,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./ScriptingBox.scss";
import { OverlayView } from "../OverlayView";
import { DocumentIconContainer } from "./DocumentIcon";
-import { List } from "../../../new_fields/List";
+import { List } from "../../../fields/List";
const ScriptingSchema = createSchema({});
type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>;
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index cb2526769..9a1aefba9 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -4,10 +4,10 @@ import { runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { documentSchema } from '../../../fields/documentSchemas';
+import { createSchema, makeInterface } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxBaseComponent } from '../DocComponent';
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9602eac52..9d02239fc 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,16 +1,13 @@
import React = require("react");
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { faVideo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked } from "mobx";
import { observer } from "mobx-react";
import * as rp from 'request-promise';
-import { Doc } from "../../../new_fields/Doc";
-import { InkTool } from "../../../new_fields/InkField";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { VideoField } from "../../../new_fields/URLField";
+import { Doc } from "../../../fields/Doc";
+import { InkTool } from "../../../fields/InkField";
+import { createSchema, makeInterface } from "../../../fields/Schema";
+import { Cast, StrCast } from "../../../fields/Types";
+import { VideoField } from "../../../fields/URLField";
import { Utils, emptyFunction, returnOne, returnZero } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -21,7 +18,8 @@ import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
import "./VideoBox.scss";
-import { documentSchema } from "../../../new_fields/documentSchemas";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { Networking } from "../../Network";
const path = require('path');
export const timeSchema = createSchema({
@@ -30,8 +28,6 @@ export const timeSchema = createSchema({
type VideoDocument = makeInterface<[typeof documentSchema, typeof timeSchema]>;
const VideoDocument = makeInterface(documentSchema, timeSchema);
-library.add(faVideo);
-
@observer
export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
static _youtubeIframeCounter: number = 0;
@@ -104,41 +100,59 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
canvas.height = 640 * (this.layoutDoc._nativeHeight || 0) / (this.layoutDoc._nativeWidth || 1);
const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
if (ctx) {
- ctx.rect(0, 0, canvas.width, canvas.height);
- ctx.fillStyle = "blue";
- ctx.fill();
+ // ctx.rect(0, 0, canvas.width, canvas.height);
+ // ctx.fillStyle = "blue";
+ // ctx.fill();
this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
}
- if (!this._videoRef) { // can't find a way to take snapshots of videos
- const b = Docs.Create.ButtonDocument({
+ if (!this._videoRef) {
+ const b = Docs.Create.LabelDocument({
x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1),
- _width: 150, _height: 50, title: (this.layoutDoc.currentTimecode || 0).toString()
+ _width: 150, _height: 50, title: (this.layoutDoc.currentTimecode || 0).toString(),
+ });
+ b.isLinkButton = true;
+ this.props.addDocument?.(b);
+ DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
+ Networking.PostToServer("/youtubeScreenshot", {
+ id: this.youtubeVideoId,
+ timecode: this.layoutDoc.currentTimecode
+ }).then(response => {
+ const resolved = response?.accessPaths?.agnostic?.client;
+ if (resolved) {
+ this.props.removeDocument?.(b);
+ this.createRealSummaryLink(resolved);
+ }
});
- b.onClick = ScriptField.MakeScript(`this.currentTimecode = ${(this.layoutDoc.currentTimecode || 0)}`);
} else {
//convert to desired file format
const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
// if you want to preview the captured image,
const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc.currentTimecode || 0).toString().replace(/\./, "_")));
- VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
+ VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => {
if (returnedFilename) {
- const url = this.choosePath(Utils.prepend(returnedFilename));
- const imageSummary = Docs.Create.ImageDocument(url, {
- _nativeWidth: this.layoutDoc._nativeWidth, _nativeHeight: this.layoutDoc._nativeHeight,
- x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
- _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc.currentTimecode || 0) + " image-"
- });
- Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
- Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
- imageSummary.isLinkButton = true;
- this.props.addDocument && this.props.addDocument(imageSummary);
- DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot");
+ this.createRealSummaryLink(returnedFilename);
}
});
}
}
+ private createRealSummaryLink = (relative: string) => {
+ const url = this.choosePath(Utils.prepend(relative));
+ const width = (this.layoutDoc._width || 0);
+ const height = (this.layoutDoc._height || 0);
+ const imageSummary = Docs.Create.ImageDocument(url, {
+ _nativeWidth: this.layoutDoc._nativeWidth, _nativeHeight: this.layoutDoc._nativeHeight,
+ x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
+ _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc.currentTimecode || 0) + " image-"
+ });
+ Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
+ Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
+ imageSummary.isLinkButton = true;
+ this.props.addDocument?.(imageSummary);
+ DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot");
+ }
+
@action
updateTimecode = () => {
this.player && (this.layoutDoc.currentTimecode = this.player.currentTime);
@@ -162,8 +176,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
componentWillUnmount() {
this.Pause();
- this._reactionDisposer && this._reactionDisposer();
- this._youtubeReactionDisposer && this._youtubeReactionDisposer();
+ this._reactionDisposer?.();
+ this._youtubeReactionDisposer?.();
}
@action
@@ -337,7 +351,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
}
@action.bound
- addDocumentWithTimestamp(doc: Doc|Doc[]): boolean {
+ addDocumentWithTimestamp(doc: Doc | Doc[]): boolean {
const docs = doc instanceof Doc ? [doc] : doc;
docs.forEach(doc => {
const curTime = (this.layoutDoc.currentTimecode || -1);
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 4c05d4627..4623444b9 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -5,6 +5,36 @@
transform-origin: top left;
width: 100%;
height: 100%;
+
+ .webBox-htmlSpan {
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+ .webBox-cont {
+ pointer-events: none;
+ }
+ .webBox-cont, .webBox-cont-interactive {
+ padding: 0vw;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ transform-origin: top left;
+ overflow: auto;
+ .webBox-iframe {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top:0;
+ }
+ }
+ .webBox-cont-interactive {
+ span {
+ user-select: text !important;
+ }
+ }
.webBox-outerContent {
width: 100%;
height: 100%;
@@ -20,29 +50,7 @@
display:none;
}
}
-.webBox-cont {
- padding: 0vw;
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- transform-origin: top left;
- overflow: auto;
- pointer-events: none;
-}
-
-.webBox-cont-interactive {
- span {
- user-select: text !important;
- }
-}
-#webBox-htmlSpan {
- position: absolute;
- top: 0;
- left: 0;
-}
.webBox-overlay {
width: 100%;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 384a6e8a5..c4ab3c9e2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,14 +1,14 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faStickyNote, faPen, faMousePointer } from '@fortawesome/free-solid-svg-icons';
-import { action, computed, observable, trace, IReactionDisposer, reaction } from "mobx";
+import { action, computed, observable, trace, IReactionDisposer, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, FieldResult } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { HtmlField } from "../../../new_fields/HtmlField";
-import { InkTool } from "../../../new_fields/InkField";
-import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types";
-import { WebField } from "../../../new_fields/URLField";
+import { Doc, FieldResult, DocListCast } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { HtmlField } from "../../../fields/HtmlField";
+import { InkTool } from "../../../fields/InkField";
+import { makeInterface, listSpec } from "../../../fields/Schema";
+import { Cast, NumCast, BoolCast, StrCast } from "../../../fields/Types";
+import { WebField } from "../../../fields/URLField";
import { Utils, returnOne, emptyFunction, returnZero } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
@@ -22,6 +22,10 @@ import React = require("react");
import * as WebRequest from 'web-request';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { undoBatch } from "../../util/UndoManager";
+import { List } from "../../../fields/List";
const htmlToText = require("html-to-text");
library.add(faStickyNote);
@@ -33,7 +37,7 @@ const WebDocument = makeInterface(documentSchema);
export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
- get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) === "disabled"; }
+ get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; }
set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; }
@observable private _url: string = "hello";
@observable private _pressX: number = 0;
@@ -48,19 +52,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
iframeLoaded = action((e: any) => {
- if (this._iframeRef.current?.contentDocument) {
- this._iframeRef.current.contentDocument.addEventListener('pointerdown', this.iframedown, false);
- this._iframeRef.current.contentDocument.addEventListener('scroll', this.iframeScrolled, false);
- this.layoutDoc.scrollHeight = this._iframeRef.current.contentDocument.children?.[0].scrollHeight || 1000;
- this._iframeRef.current.contentDocument.children[0].scrollTop = NumCast(this.layoutDoc.scrollTop);
+ const iframe = this._iframeRef.current;
+ if (iframe && iframe.contentDocument) {
+ iframe.setAttribute("enable-annotation", "true");
+ iframe.contentDocument.addEventListener('pointerdown', this.iframedown, false);
+ iframe.contentDocument.addEventListener('scroll', this.iframeScrolled, false);
+ this.layoutDoc.scrollHeight = iframe.contentDocument.children?.[0].scrollHeight || 1000;
+ iframe.contentDocument.children[0].scrollTop = NumCast(this.layoutDoc.scrollTop);
+ iframe.contentDocument.children[0].scrollLeft = NumCast(this.layoutDoc.scrollLeft);
}
this._reactionDisposer?.();
- this._reactionDisposer = reaction(() => this.layoutDoc.scrollY,
- (scrollY) => {
- if (scrollY !== undefined) {
- this._outerRef.current!.scrollTop = scrollY;
+ this._reactionDisposer = reaction(() => ({ y: this.layoutDoc.scrollY, x: this.layoutDoc.scrollX }),
+ ({ x, y }) => {
+ if (y !== undefined) {
+ this._outerRef.current!.scrollTop = y;
this.layoutDoc.scrollY = undefined;
}
+ if (x !== undefined) {
+ this._outerRef.current!.scrollLeft = x;
+ this.layoutDoc.scrollX = undefined;
+ }
},
{ fireImmediately: true }
);
@@ -70,14 +81,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
this._setPreviewCursor?.(e.screenX, e.screenY, false);
}
iframeScrolled = (e: any) => {
- const scroll = e.target?.children?.[0].scrollTop;
- this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scroll;
+ const scrollTop = e.target?.children?.[0].scrollTop;
+ const scrollLeft = e.target?.children?.[0].scrollLeft;
+ this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scrollTop;
+ this.layoutDoc.scrollLeft = this._outerRef.current!.scrollLeft = scrollLeft;
}
async componentDidMount() {
-
- this.setURL();
-
- this._iframeRef.current!.setAttribute("enable-annotation", "true");
+ const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField);
+ runInAction(() => this._url = urlField?.url.toString() || "");
document.addEventListener("pointerup", this.onLongPressUp);
document.addEventListener("pointermove", this.onLongPressMove);
@@ -86,14 +97,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
const youtubeaspect = 400 / 315;
const nativeWidth = NumCast(this.layoutDoc._nativeWidth);
const nativeHeight = NumCast(this.layoutDoc._nativeHeight);
- if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
- if (!nativeWidth) this.layoutDoc._nativeWidth = 600;
- this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / youtubeaspect;
- this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
- }
+ if (field) {
+ if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
+ if (!nativeWidth) this.layoutDoc._nativeWidth = 600;
+ this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / youtubeaspect;
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
+ }
+ } // else it's an HTMLfield
} else if (field?.url) {
const result = await WebRequest.get(Utils.CorsProxy(field.url.href));
- this.dataDoc.text = htmlToText.fromString(result.content);
+ if (result) {
+ this.dataDoc.text = htmlToText.fromString(result.content);
+ }
}
}
@@ -101,8 +116,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
this._reactionDisposer?.();
document.removeEventListener("pointerup", this.onLongPressUp);
document.removeEventListener("pointermove", this.onLongPressMove);
- this._iframeRef.current!.contentDocument?.removeEventListener('pointerdown', this.iframedown);
- this._iframeRef.current!.contentDocument?.removeEventListener('scroll', this.iframeScrolled);
+ this._iframeRef.current?.contentDocument?.removeEventListener('pointerdown', this.iframedown);
+ this._iframeRef.current?.contentDocument?.removeEventListener('scroll', this.iframeScrolled);
}
@action
@@ -110,16 +125,73 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
this._url = e.target.value;
}
+ onUrlDragover = (e: React.DragEvent) => {
+ e.preventDefault();
+ }
@action
- submitURL = () => {
- this.dataDoc[this.props.fieldKey] = new WebField(new URL(this._url));
+ onUrlDrop = (e: React.DragEvent) => {
+ const { dataTransfer } = e;
+ const html = dataTransfer.getData("text/html");
+ const uri = dataTransfer.getData("text/uri-list");
+ const url = uri || html || this._url;
+ this._url = url.startsWith(window.location.origin) ?
+ url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url;
+ this.submitURL();
+ e.stopPropagation();
+ }
+
+ @action
+ forward = () => {
+ const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null);
+ const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), null);
+ if (future.length) {
+ history.push(this._url);
+ this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(this.dataDoc[this.annotationKey]));
+ this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!));
+ this.dataDoc[this.annotationKey] = new List<Doc>(DocListCast(this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)]));
+ }
}
@action
- setURL() {
- const urlField: FieldResult<WebField> = Cast(this.dataDoc[this.props.fieldKey], WebField);
- if (urlField) this._url = urlField.url.toString();
- else this._url = "";
+ back = () => {
+ const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null);
+ const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), null);
+ if (history.length) {
+ if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
+ else future.push(this._url);
+ this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(this.dataDoc[this.annotationKey]));
+ this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!));
+ this.dataDoc[this.annotationKey] = new List<Doc>(DocListCast(this.dataDoc[this.annotationKey + "-" + this.urlHash(this._url)]));
+ }
+ }
+
+ urlHash(s: string) {
+ return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0);
+ }
+ @action
+ submitURL = () => {
+ if (!this._url.startsWith("http")) this._url = "http://" + this._url;
+ try {
+ const URLy = new URL(this._url);
+ const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null);
+ const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), null);
+ const annos = DocListCast(this.dataDoc[this.annotationKey]);
+ const url = Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.toString();
+ if (url) {
+ if (history === undefined) {
+ this.dataDoc[this.fieldKey + "-history"] = new List<string>([url]);
+
+ } else {
+ history.push(url);
+ }
+ future && (future.length = 0);
+ this.dataDoc[this.annotationKey + "-" + this.urlHash(url)] = new List<Doc>(annos);
+ }
+ this.dataDoc[this.fieldKey] = new WebField(URLy);
+ this.dataDoc[this.annotationKey] = new List<Doc>([]);
+ } catch (e) {
+ console.log("Error in URL :" + this._url);
+ }
}
onValueKeyDown = async (e: React.KeyboardEvent) => {
@@ -130,19 +202,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
toggleAnnotationMode = () => {
- if (!this.layoutDoc.isAnnotating) {
- this.layoutDoc.lockedTransform = false;
- this.layoutDoc.isAnnotating = true;
- }
- else {
- this.layoutDoc.lockedTransform = true;
- this.layoutDoc.isAnnotating = false;
- }
+ this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating;
}
urlEditor() {
return (
- <div className="webBox-urlEditor" style={{ top: this._collapsed ? -70 : 0 }}>
+ <div className="webBox-urlEditor"
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover} style={{ top: this._collapsed ? -70 : 0 }}>
<div className="urlEditor">
<div className="editorBase">
<button className="editor-collapse"
@@ -155,7 +222,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
title="Collapse Url Editor" onClick={this.toggleCollapse}>
<FontAwesomeIcon icon="caret-up" size="2x" />
</button>
- <div className="webBox-buttons" style={{ display: this._collapsed ? "none" : "flex" }}>
+ <div className="webBox-buttons"
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover} style={{ display: this._collapsed ? "none" : "flex" }}>
<div className="webBox-freeze" title={"Annotate"} style={{ background: this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleAnnotationMode} >
<FontAwesomeIcon icon={faPen} size={"2x"} />
</div>
@@ -165,6 +234,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
<input className="webpage-urlInput"
placeholder="ENTER URL"
value={this._url}
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover}
onChange={this.onURLChange}
onKeyDown={this.onValueKeyDown}
/>
@@ -172,10 +243,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
- minWidth: "100px",
+ maxWidth: "120px",
}}>
- <button className="submitUrl" onClick={this.submitURL}>
- SUBMIT
+ <button className="submitUrl" onClick={this.submitURL}
+ onDragOver={this.onUrlDragover} onDrop={this.onUrlDrop}>
+ GO
+ </button>
+ <button className="submitUrl" onClick={this.back}>
+ <FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon>
+ </button>
+ <button className="submitUrl" onClick={this.forward}>
+ <FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon>
</button>
</div>
</div>
@@ -309,19 +387,39 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
+ @undoBatch
+ @action
+ toggleNativeDimensions = () => {
+ Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+ }
+ specificContextMenu = (e: React.MouseEvent): void => {
+ const cm = ContextMenu.Instance;
+ const funcs: ContextMenuProps[] = [];
+ funcs.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
+ cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+
+ }
+
+ //const href = "https://brown365-my.sharepoint.com/personal/bcz_ad_brown_edu/_layouts/15/Doc.aspx?sourcedoc={31aa3178-4c21-4474-b367-877d0a7135e4}&action=embedview&wdStartOn=1";
+
@computed
- get content() {
+ get urlContent() {
+
const field = this.dataDoc[this.props.fieldKey];
let view;
if (field instanceof HtmlField) {
- view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
+ view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
- view = <iframe ref={this._iframeRef} onLoad={this.iframeLoaded} src={url} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+ view = <iframe className="webBox-iframe" enable-annotation={true} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded} />;
} else {
- view = <iframe ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+ view = <iframe className="webBox-iframe" enable-annotation={true} ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} />;
}
-
+ return view;
+ }
+ @computed
+ get content() {
+ const view = this.urlContent;
const decInteracting = DocumentDecorations.Instance?.Interacting;
const frozen = !this.props.isSelected() || decInteracting;
@@ -342,7 +440,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
{this.urlEditor()}
</>);
}
- scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop));
+ scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc.scrollLeft), NumCast(this.layoutDoc.scrollTop));
render() {
return (<div className={`webBox-container`}
style={{
@@ -350,18 +448,27 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}%` : "100%",
height: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}%` : "100%",
pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
- }} >
+ }}
+ onContextMenu={this.specificContextMenu}>
+ <base target="_blank" />
{this.content}
<div className={"webBox-outerContent"} ref={this._outerRef}
style={{ pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none" }}
onWheel={e => e.stopPropagation()}
onScroll={e => {
- if (this._iframeRef.current!.contentDocument!.children[0].scrollTop !== this._outerRef.current!.scrollTop) {
- this._iframeRef.current!.contentDocument!.children[0].scrollTop = this._outerRef.current!.scrollTop;
+ const iframe = this._iframeRef?.current?.contentDocument;
+ const outerFrame = this._outerRef.current;
+ if (iframe && outerFrame) {
+ if (iframe.children[0].scrollTop !== outerFrame.scrollTop) {
+ iframe.children[0].scrollTop = outerFrame.scrollTop;
+ }
+ if (iframe.children[0].scrollLeft !== outerFrame.scrollLeft) {
+ iframe.children[0].scrollLeft = outerFrame.scrollLeft;
+ }
}
//this._outerRef.current!.scrollTop !== this._scrollTop && (this._outerRef.current!.scrollTop = this._scrollTop)
}}>
- <div className={"webBox-innerContent"} style={{ height: NumCast(this.layoutDoc.scrollHeight) }}>
+ <div className={"webBox-innerContent"} style={{ height: NumCast(this.layoutDoc.scrollHeight), width: 4000 }}>
<CollectionFreeFormView {...this.props}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index d94fe7fc6..d56b87ae5 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -8,14 +8,14 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s
import { StepMap } from "prosemirror-transform";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
-import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { List } from "../../../../new_fields/List";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { listSpec } from "../../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { List } from "../../../../fields/List";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { listSpec } from "../../../../fields/Schema";
+import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 7130fee2b..05e6a5959 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,10 +1,10 @@
import { IReactionDisposer, reaction } from "mobx";
import { NodeSelection } from "prosemirror-state";
-import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Doc, HeightSym, WidthSym } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs } from "../../../documents/Documents";
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 3c6841f08..d05e8f1ea 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,10 +1,10 @@
import { IReactionDisposer, observable, runInAction, computed, action } from "mobx";
-import { Doc, DocListCast, Field } from "../../../../new_fields/Doc";
-import { List } from "../../../../new_fields/List";
-import { listSpec } from "../../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../../new_fields/Types";
+import { Doc, DocListCast, Field } from "../../../../fields/Doc";
+import { List } from "../../../../fields/List";
+import { listSpec } from "../../../../fields/Schema";
+import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { Cast, StrCast } from "../../../../fields/Types";
import { DocServer } from "../../../DocServer";
import { CollectionViewType } from "../../collections/CollectionView";
import { FormattedTextBox } from "./FormattedTextBox";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 23bf86a32..fc131cd38 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,17 +12,17 @@ import { Fragment, Mark, Node, Slice } from "prosemirror-model";
import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state";
import { ReplaceStep } from 'prosemirror-transform';
import { EditorView } from "prosemirror-view";
-import { DateField } from '../../../../new_fields/DateField';
-import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
-import { documentSchema } from '../../../../new_fields/documentSchemas';
-import { Id } from '../../../../new_fields/FieldSymbols';
-import { InkTool } from '../../../../new_fields/InkField';
-import { PrefetchProxy } from '../../../../new_fields/Proxy';
-import { RichTextField } from "../../../../new_fields/RichTextField";
-import { RichTextUtils } from '../../../../new_fields/RichTextUtils';
-import { createSchema, makeInterface } from "../../../../new_fields/Schema";
-import { Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
-import { TraceMobx } from '../../../../new_fields/util';
+import { DateField } from '../../../../fields/DateField';
+import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../../fields/Doc";
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkTool } from '../../../../fields/InkField';
+import { PrefetchProxy } from '../../../../fields/Proxy';
+import { RichTextField } from "../../../../fields/RichTextField";
+import { RichTextUtils } from '../../../../fields/RichTextUtils';
+import { createSchema, makeInterface } from "../../../../fields/Schema";
+import { Cast, DateCast, NumCast, StrCast } from "../../../../fields/Types";
+import { TraceMobx } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from "../../../DocServer";
@@ -58,7 +58,7 @@ import { FieldView, FieldViewProps } from "../FieldView";
import "./FormattedTextBox.scss";
import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment';
import React = require("react");
-import { ScriptField } from '../../../../new_fields/ScriptField';
+import { ScriptField } from '../../../../fields/ScriptField';
import GoogleAuthenticationManager from '../../../apis/GoogleAuthenticationManager';
library.add(faEdit);
@@ -206,6 +206,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
if (json !== curLayout?.Data) {
!curText && tx.storedMarks?.map(m => m.type.name === "pFontSize" && (Doc.UserDoc().fontSize = this.layoutDoc._fontSize = m.attrs.fontSize));
+ !curText && tx.storedMarks?.map(m => m.type.name === "pFontFamily" && (Doc.UserDoc().fontFamily = this.layoutDoc._fontFamily = m.attrs.fontFamily));
this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
}
@@ -868,7 +869,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
const marks = [...node.marks];
const linkIndex = marks.findIndex(mark => mark.type.name === "link");
- const link = view.state.schema.mark(view.state.schema.marks.link, { href: `http://localhost:1050/doc/${linkId}`, location: "onRight", title: title, docref: true });
+ const link = view.state.schema.mark(view.state.schema.marks.link, { href: Utils.prepend(`/doc/${linkId}`), location: "onRight", title: title, docref: true });
marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
return node.mark(marks);
}
@@ -1178,7 +1179,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@action
tryUpdateHeight(limitHeight?: number) {
let scrollHeight = this._ref.current?.scrollHeight;
- if (this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+ if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
scrollHeight = scrollHeight * NumCast(this.layoutDoc.scale, 1);
if (limitHeight && scrollHeight > limitHeight) {
scrollHeight = limitHeight;
@@ -1210,16 +1211,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
render() {
TraceMobx();
- const style: { [key: string]: any } = {};
const scale = this.props.ContentScaling() * NumCast(this.layoutDoc.scale, 1);
- const divKeys = ["width", "height", "background"];
- const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value
- return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.rootDoc, this: this.layoutDoc }).result as string || "";
- };
- divKeys.map((prop: string) => {
- const p = (this.props as any)[prop] as string;
- p && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
- });
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
const interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground;
if (this.props.isSelected()) {
@@ -1232,19 +1224,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
transform: `scale(${scale})`,
transformOrigin: "top left",
width: `${100 / scale}%`,
- height: `${100 / scale}%`,
+ height: `calc(${100 / scale}% - ${this.props.ChromeHeight?.() || 0}px)`,
+ ...this.styleFromLayoutString(scale)
}}>
<div className={`formattedTextBox-cont`} ref={this._ref}
style={{
width: "100%",
- height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : `calc(100% - ${this.props.ChromeHeight?.() || 0}px`,
- background: this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""),
+ height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined,
+ background: Doc.UserDoc().renderStyle === "comic" ? "transparent" : this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""),
opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
pointerEvents: interactive ? "none" : undefined,
fontSize: Cast(this.layoutDoc._fontSize, "number", null),
- fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
- ...style
+ fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit")
}}
onContextMenu={this.specificContextMenu}
onKeyDown={this.onKeyPress}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index a33717855..d47ae63af 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -2,8 +2,8 @@ import { Mark, ResolvedPos } from "prosemirror-model";
import { EditorState, Plugin } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
-import { Doc, DocCastAsync } from "../../../../new_fields/Doc";
-import { Cast, FieldValue, NumCast } from "../../../../new_fields/Types";
+import { Doc, DocCastAsync } from "../../../../fields/Doc";
+import { Cast, FieldValue, NumCast } from "../../../../fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { DocumentManager } from "../../../util/DocumentManager";
@@ -211,8 +211,8 @@ export class FormattedTextBoxComment {
NativeWidth={returnZero}
NativeHeight={returnZero}
/>, FormattedTextBoxComment.tooltipText);
- FormattedTextBoxComment.tooltip.style.width = NumCast(target.width) ? `${NumCast(target.width)}` : "100%";
- FormattedTextBoxComment.tooltip.style.height = NumCast(target.height) ? `${NumCast(target.height)}` : "100%";
+ FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%";
+ FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%";
}
// let ext = (target && target.type !== DocumentType.PDFANNO && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field. probably need an 'overviewLayout' and then just display the target Document ....
// let text = ext && StrCast(ext.text);
diff --git a/src/client/views/nodes/formattedText/ImageResizeView.tsx b/src/client/views/nodes/formattedText/ImageResizeView.tsx
index 8f98da0fd..401ecd7e6 100644
--- a/src/client/views/nodes/formattedText/ImageResizeView.tsx
+++ b/src/client/views/nodes/formattedText/ImageResizeView.tsx
@@ -1,5 +1,5 @@
import { NodeSelection } from "prosemirror-state";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
import { DocServer } from "../../../DocServer";
import { DocumentManager } from "../../../util/DocumentManager";
import React = require("react");
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index a0b02880e..0e3e7f91e 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,10 +7,10 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
import { EditorState, Transaction, TextSelection } from "prosemirror-state";
import { SelectionManager } from "../../../util/SelectionManager";
import { Docs } from "../../../documents/Documents";
-import { NumCast, BoolCast, Cast, StrCast } from "../../../../new_fields/Types";
-import { Doc } from "../../../../new_fields/Doc";
+import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
+import { Doc } from "../../../../fields/Doc";
import { FormattedTextBox } from "./FormattedTextBox";
-import { Id } from "../../../../new_fields/FieldSymbols";
+import { Id } from "../../../../fields/FieldSymbols";
const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -154,15 +154,12 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
const originalDoc = layoutDoc.rootDocument || layoutDoc;
if (originalDoc instanceof Doc) {
const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Docs.Create.TextDocument("", {
- layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout,
- layoutKey,
- _singleLine: BoolCast(originalDoc._singleLine),
- x: NumCast(originalDoc.x), y: NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10, _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height)
- });
+ const newDoc = Doc.MakeCopy(originalDoc, true);
+ newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = originalDoc[layoutKey];
}
+ Doc.GetProto(newDoc).text = undefined;
FormattedTextBox.SelectOnLoad = newDoc[Id];
props.addDocument(newDoc);
}
@@ -178,15 +175,12 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
const originalDoc = layoutDoc.rootDocument || layoutDoc;
if (force || props.Document._singleLine) {
const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Docs.Create.TextDocument("", {
- layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout,
- layoutKey,
- _singleLine: BoolCast(originalDoc._singleLine),
- x: NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10, y: NumCast(originalDoc.y), _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height)
- });
+ const newDoc = Doc.MakeCopy(originalDoc, true);
+ newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = originalDoc[layoutKey];
}
+ Doc.GetProto(newDoc).text = undefined;
FormattedTextBox.SelectOnLoad = newDoc[Id];
props.addDocument(newDoc);
return true;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 170a39801..fd1b26208 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -11,14 +11,14 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller, faSleigh } from "@fortawesome/free-solid-svg-icons";
import { updateBullets } from "./ProsemirrorExampleTransfer";
import { FieldViewProps } from "../FieldView";
-import { Cast, StrCast } from "../../../../new_fields/Types";
+import { Cast, StrCast } from "../../../../fields/Types";
import { FormattedTextBoxProps } from "./FormattedTextBox";
import { unimplementedFunction, Utils } from "../../../../Utils";
import { wrapInList } from "prosemirror-schema-list";
-import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../new_fields/SchemaHeaderField';
+import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
import "./RichTextMenu.scss";
import { DocServer } from "../../../DocServer";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
import { SelectionManager } from "../../../util/SelectionManager";
import { LinkManager } from "../../../util/LinkManager";
const { toggleMark, setBlockType } = require("prosemirror-commands");
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 0ba591fec..fbd6c87bb 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -1,9 +1,9 @@
import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from "prosemirror-inputrules";
import { NodeSelection, TextSelection } from "prosemirror-state";
-import { DataSym, Doc } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { Cast, NumCast } from "../../../../new_fields/Types";
+import { DataSym, Doc } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { Cast, NumCast } from "../../../../fields/Types";
import { returnFalse, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
@@ -11,7 +11,7 @@ import { FormattedTextBox } from "./FormattedTextBox";
import { wrappingInputRule } from "./prosemirrorPatches";
import RichTextMenu from "./RichTextMenu";
import { schema } from "./schema_rts";
-import { List } from "../../../../new_fields/List";
+import { List } from "../../../../fields/List";
export class RichTextRules {
public Document: Doc;
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 7edd191cf..91280dea4 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -8,14 +8,14 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s
import { StepMap } from "prosemirror-transform";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
-import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { Id } from "../../../../new_fields/FieldSymbols";
-import { List } from "../../../../new_fields/List";
-import { ObjectField } from "../../../../new_fields/ObjectField";
-import { listSpec } from "../../../../new_fields/Schema";
-import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../../new_fields/Types";
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+import { List } from "../../../../fields/List";
+import { ObjectField } from "../../../../fields/ObjectField";
+import { listSpec } from "../../../../fields/Schema";
+import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+import { ComputedField } from "../../../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../../fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs } from "../../../documents/Documents";
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 46bf481fb..ebaa23e99 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -1,6 +1,6 @@
import React = require("react");
import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../fields/Doc";
const emDOM: DOMOutputSpecArray = ["em", 0];
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 672d3adb8..cb6a15f36 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,10 +1,10 @@
import React = require("react");
import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { List } from "../../../new_fields/List";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { List } from "../../../fields/List";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
import { DocumentManager } from "../../util/DocumentManager";
import PDFMenu from "./PDFMenu";
import "./Annotation.scss";
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 2a6eff7ff..ff328068b 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { unimplementedFunction, returnFalse } from "../../../Utils";
import AntimodeMenu from "../AntimodeMenu";
-import { Doc, Opt } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../fields/Doc";
@observer
export default class PDFMenu extends AntimodeMenu {
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 8541a3149..affffc44e 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -29,6 +29,7 @@
.page {
position: relative;
+ border: unset;
}
.pdfViewer-text-selected {
.textLayer{
@@ -57,6 +58,9 @@
display: inline-block;
width:100%;
}
+ .pdfViewer-overlay {
+ pointer-events: none;
+ }
.pdfViewer-annotationLayer {
position: absolute;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index acaa4363e..5bad248be 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,16 +4,16 @@ import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import * as rp from "request-promise";
import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { InkTool } from "../../../new_fields/InkField";
-import { List } from "../../../new_fields/List";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, NumCast } from "../../../new_fields/Types";
-import { PdfField } from "../../../new_fields/URLField";
-import { TraceMobx } from "../../../new_fields/util";
+import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { Id } from "../../../fields/FieldSymbols";
+import { InkTool } from "../../../fields/InkField";
+import { List } from "../../../fields/List";
+import { createSchema, makeInterface } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, NumCast } from "../../../fields/Types";
+import { PdfField } from "../../../fields/URLField";
+import { TraceMobx } from "../../../fields/util";
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
@@ -31,8 +31,10 @@ import Annotation from "./Annotation";
import PDFMenu from "./PDFMenu";
import "./PDFViewer.scss";
import React = require("react");
+import { SnappingManager } from "../../util/SnappingManager";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
+import { Networking } from "../../Network";
export const pageSchema = createSchema({
curPage: "number",
@@ -125,12 +127,20 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
_lastSearch: string = "";
componentDidMount = async () => {
- !this.props.Document.lockedTransform && (this.props.Document.lockedTransform = true);
// change the address to be the file address of the PNG version of each page
// file address of the pdf
const { url: { href } } = Cast(this.dataDoc[this.props.fieldKey], PdfField)!;
- const addr = Utils.prepend(`/thumbnail${this.props.url.substring("files/pdfs/".length, this.props.url.length - ".pdf".length)}-${(this.Document.curPage || 1)}.png`);
- this._coverPath = href.startsWith(window.location.origin) ? JSON.parse(await rp.get(addr)) : { width: 100, height: 100, path: "" };
+ const { url: relative } = this.props;
+ const pathComponents = relative.split("/pdfs/")[1].split("/");
+ const coreFilename = pathComponents.pop()!.split(".")[0];
+ const params: any = {
+ coreFilename,
+ pageNum: this.Document.curPage || 1,
+ };
+ if (pathComponents.length) {
+ params.subtree = `${pathComponents.join("/")}/`;
+ }
+ this._coverPath = href.startsWith(window.location.origin) ? await Networking.PostToServer("/thumbnail", params) : { width: 100, height: 100, path: "" };
runInAction(() => this._showWaiting = this._showCover = true);
this.props.startupLive && this.setupPdfJsViewer();
this._searchReactionDisposer = reaction(() => this.Document.searchMatch, search => {
@@ -642,7 +652,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
panelWidth = () => (this.Document.scrollHeight || this.Document._nativeHeight || 0);
panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
@computed get overlayLayer() {
- return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : ""}`} id="overlay"
+ return <div className={`pdfViewer-overlay${InkingControl.Instance.selectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} id="overlay"
style={{ transform: `scale(${this._zoomed})` }}>
<CollectionFreeFormView {...this.props}
LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
@@ -662,6 +672,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
ContentScaling={this.contentZoom}
bringToFront={emptyFunction}
whenActiveChanged={this.whenActiveChanged}
+ childPointerEvents={true}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
addDocument={this.addDocument}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index e13a5f2f7..caee06d8f 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -1,12 +1,12 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DataSym, DocListCast } from "../../../new_fields/Doc";
-import { documentSchema } from '../../../new_fields/documentSchemas';
-import { Id } from "../../../new_fields/FieldSymbols";
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
-import { Cast, NumCast, BoolCast, ScriptCast } from "../../../new_fields/Types";
-import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero } from "../../../Utils";
+import { Doc, DataSym, DocListCast } from "../../../fields/Doc";
+import { documentSchema } from '../../../fields/documentSchemas';
+import { Id } from "../../../fields/FieldSymbols";
+import { createSchema, makeInterface, listSpec } from '../../../fields/Schema';
+import { Cast, NumCast, BoolCast, ScriptCast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero, numberRange } from "../../../Utils";
import { Transform } from "../../util/Transform";
import { CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from '../DocComponent';
@@ -14,6 +14,7 @@ import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./PresElementBox.scss";
import React = require("react");
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
export const presSchema = createSchema({
presentationTargetDoc: Doc,
@@ -43,6 +44,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
@computed get collapsedHeight() { return Number(this.lookupField("presCollapsedHeight")); } // the collapsed height changes depending on the state of the presBox. We could store this on the presentation elemnt template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
@computed get presStatus() { return BoolCast(this.lookupField("presStatus")); }
@computed get itemIndex() { return NumCast(this.lookupField("_itemIndex")); }
+ @computed get presBox() { return Cast(this.lookupField("presBox"), Doc, null); }
@computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; }
componentDidMount() {
@@ -93,6 +95,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
}
}
+ @action
+ progressivize = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ this.rootDoc.presProgressivize = !this.rootDoc.presProgressivize;
+ const rootTarget = Cast(this.rootDoc.presentationTargetDoc, Doc, null);
+ const docs = DocListCast(rootTarget[Doc.LayoutFieldKey(rootTarget)]);
+ if (this.rootDoc.presProgressivize) {
+ rootTarget.currentFrame = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true);
+ rootTarget.lastFrame = docs.length - 1;
+ }
+ }
+
/**
* The function that is called on click to turn fading document after presented option on/off.
* It also makes sure that the option swithches from hide-after to this one, since both
@@ -163,6 +178,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
LibraryPath={emptyPath}
fitToBox={true}
+ backgroundColor={this.props.backgroundColor}
rootSelected={returnTrue}
addDocument={returnFalse}
removeDocument={returnFalse}
@@ -177,6 +193,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
focus={emptyFunction}
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
+ opacity={returnOne}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
ContentScaling={returnOne}
@@ -199,7 +216,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
<strong className="presElementBox-name">
{`${this.indexInPres + 1}. ${this.targetDoc?.title}`}
</strong>
- <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => this.props.removeDocument?.(this.rootDoc)}>X</button>
+ <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => {
+ this.props.removeDocument?.(this.rootDoc);
+ e.stopPropagation();
+ }}>X</button>
<br />
</>}
<div className="presElementBox-buttons">
@@ -209,6 +229,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
<button title="Fade After" className={pbi + (this.rootDoc.presFadeButton ? "-selected" : "")} onClick={this.onFadeDocumentAfterPresentedClick}><FontAwesomeIcon icon={"file-download"} onPointerDown={e => e.stopPropagation()} /></button>
<button title="Hide After" className={pbi + (this.rootDoc.presHideAfterButton ? "-selected" : "")} onClick={this.onHideDocumentAfterPresentedClick}><FontAwesomeIcon icon={"file-download"} onPointerDown={e => e.stopPropagation()} /></button>
<button title="Group With Up" className={pbi + (this.rootDoc.presGroupButton ? "-selected" : "")} onClick={e => { e.stopPropagation(); this.rootDoc.presGroupButton = !this.rootDoc.presGroupButton; }}><FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} /></button>
+ <button title="Progressivize" className={pbi + (this.rootDoc.pres ? "-selected" : "")} onClick={this.progressivize}><FontAwesomeIcon icon={"tasks"} onPointerDown={e => e.stopPropagation()} /></button>
<button title="Expand Inline" className={pbi + (this.rootDoc.presExpandInlineButton ? "-selected" : "")} onClick={e => { e.stopPropagation(); this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton; }}><FontAwesomeIcon icon={"arrow-down"} onPointerDown={e => e.stopPropagation()} /></button>
</div>
{this.renderEmbeddedInline}
diff --git a/src/client/views/search/CheckBox.tsx b/src/client/views/search/CheckBox.tsx
index 8c97d5dbc..0a1e551ec 100644
--- a/src/client/views/search/CheckBox.tsx
+++ b/src/client/views/search/CheckBox.tsx
@@ -2,7 +2,6 @@ import * as React from 'react';
import { observer } from 'mobx-react';
import { observable, action, runInAction, IReactionDisposer, reaction } from 'mobx';
import "./CheckBox.scss";
-import * as anime from 'animejs';
interface CheckBoxProps {
originalStatus: boolean;
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index 662b37d77..4b53963a5 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -4,10 +4,10 @@ import { observable, action } from 'mobx';
import "./SearchBox.scss";
import { faTimes, faCheckCircle, faObjectGroup } from '@fortawesome/free-solid-svg-icons';
import { library } from '@fortawesome/fontawesome-svg-core';
-import { Doc } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
+import { Doc } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
import { DocumentType } from "../../documents/DocumentTypes";
-import { Cast, StrCast } from '../../../new_fields/Types';
+import { Cast, StrCast } from '../../../fields/Types';
import * as _ from "lodash";
import { IconBar } from './IconBar';
import { FieldFilters } from './FieldFilters';
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index a1631951e..7ada7574c 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -5,9 +5,9 @@ import { action, computed, observable, runInAction, IReactionDisposer, reaction
import { observer } from 'mobx-react';
import * as React from 'react';
import * as rp from 'request-promise';
-import { Doc } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { Doc } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { Utils, returnTrue, emptyFunction, returnFalse, emptyPath, returnOne, returnEmptyString } from '../../../Utils';
import { Docs, DocumentOptions } from '../../documents/Documents';
import { SetupDrag, DragManager } from '../../util/DragManager';
@@ -21,20 +21,21 @@ import { DocumentView } from '../nodes/DocumentView';
import { SelectionManager } from '../../util/SelectionManager';
import { FilterQuery } from 'mongodb';
import { CollectionLinearView } from '../collections/CollectionLinearView';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+
import { CollectionDockingView } from '../collections/CollectionDockingView';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { PrefetchProxy } from '../../../new_fields/Proxy';
-import { List } from '../../../new_fields/List';
+import { ScriptField } from '../../../fields/ScriptField';
+import { PrefetchProxy } from '../../../fields/Proxy';
+import { List } from '../../../fields/List';
import { faSearch, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia, faBan, faVideo, faCaretDown } from '@fortawesome/free-solid-svg-icons';
import { Transform } from '../../util/Transform';
import { MainView } from "../MainView";
import { Scripting } from '../../util/Scripting';
import { CollectionView, CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from "../DocComponent";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { makeInterface, createSchema } from '../../../new_fields/Schema';
-import { listSpec } from '../../../new_fields/Schema';
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface, createSchema } from '../../../fields/Schema';
+import { listSpec } from '../../../fields/Schema';
library.add(faTimes);
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 6e319b104..14aa985ae 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -4,9 +4,9 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocCastAsync } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Doc, DocCastAsync } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from "../../util/DocumentManager";
@@ -24,11 +24,11 @@ import "./SearchItem.scss";
import "./SelectorContextMenu.scss";
import { FieldViewProps, FieldView } from "../nodes/FieldView";
import { ViewBoxBaseComponent } from "../DocComponent";
-import { makeInterface, createSchema } from "../../../new_fields/Schema";
-import { documentSchema } from "../../../new_fields/documentSchemas";
-import { PrefetchProxy } from "../../../new_fields/Proxy";
+import { makeInterface, createSchema } from "../../../fields/Schema";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { PrefetchProxy } from "../../../fields/Proxy";
import { Docs } from "../../documents/Documents";
-import { ScriptField } from "../../../new_fields/ScriptField";
+import { ScriptField } from "../../../fields/ScriptField";
import { CollectionStackingView } from "../collections/CollectionStackingView";
export interface SearchItemProps {
diff --git a/src/client/views/webcam/WebCamLogic.js b/src/client/views/webcam/WebCamLogic.js
index f542fb983..a8a2f5fa4 100644
--- a/src/client/views/webcam/WebCamLogic.js
+++ b/src/client/views/webcam/WebCamLogic.js
@@ -1,5 +1,8 @@
'use strict';
import io from "socket.io-client";
+import {
+ resolvedPorts
+} from "../Main";
var socket;
var isChannelReady = false;
@@ -29,7 +32,7 @@ export function initialize(roomName, handlerUI) {
room = roomName;
- socket = io.connect(`${window.location.protocol}//${window.location.hostname}:${4321}`);
+ socket = io.connect(`${window.location.protocol}//${window.location.hostname}:${resolvedPorts.socket}`);
if (room !== '') {
socket.emit('create or join', room);
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index fd6b47ff0..be53c0b9b 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -3,10 +3,11 @@ import * as ReactDOM from 'react-dom';
import { observer } from 'mobx-react';
import { observable, computed } from 'mobx';
import { CompileScript } from '../client/util/Scripting';
-import { makeInterface } from '../new_fields/Schema';
-import { ObjectField } from '../new_fields/ObjectField';
-import { RefField } from '../new_fields/RefField';
+import { makeInterface } from '../fields/Schema';
+import { ObjectField } from '../fields/ObjectField';
+import { RefField } from '../fields/RefField';
import { DocServer } from '../client/DocServer';
+import { resolvedPorts } from '../client/views/Main';
@observer
class Repl extends React.Component {
@@ -61,6 +62,6 @@ class Repl extends React.Component {
}
(async function () {
- DocServer.init(window.location.protocol, window.location.hostname, 4321, "repl");
+ DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, "repl");
ReactDOM.render(<Repl />, document.getElementById("root"));
})(); \ No newline at end of file
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 3baedce4b..17d3db8fd 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { DocServer } from '../client/DocServer';
-import { Doc } from '../new_fields/Doc';
+import { Doc } from '../fields/Doc';
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import { Utils } from '../Utils';
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index a26d2e06a..0ca067ed3 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -3,17 +3,18 @@ import "normalize.css";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { observer } from 'mobx-react';
-import { Doc, Field, FieldResult, Opt } from '../new_fields/Doc';
+import { Doc, Field, FieldResult, Opt } from '../fields/Doc';
import { DocServer } from '../client/DocServer';
-import { Id } from '../new_fields/FieldSymbols';
-import { List } from '../new_fields/List';
-import { URLField } from '../new_fields/URLField';
+import { Id } from '../fields/FieldSymbols';
+import { List } from '../fields/List';
+import { URLField } from '../fields/URLField';
import { EditableView } from '../client/views/EditableView';
import { CompileScript } from '../client/util/Scripting';
-import { RichTextField } from '../new_fields/RichTextField';
-import { DateField } from '../new_fields/DateField';
-import { ScriptField } from '../new_fields/ScriptField';
-import CursorField from '../new_fields/CursorField';
+import { RichTextField } from '../fields/RichTextField';
+import { DateField } from '../fields/DateField';
+import { ScriptField } from '../fields/ScriptField';
+import CursorField from '../fields/CursorField';
+import { resolvedPorts } from '../client/views/Main';
DateField;
URLField;
@@ -96,7 +97,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> {
content = (
<div>
Document ({this.props.field[Id]})
- <div style={{ paddingLeft: "25px" }}>
+ <div style={{ paddingLeft: "25px" }}>
{fields}
</div>
</div>
@@ -182,7 +183,7 @@ class Viewer extends React.Component {
}
(async function () {
- await DocServer.init(window.location.protocol, window.location.hostname, 4321, "viewer");
+ await DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, "viewer");
ReactDOM.render((
<div style={{ position: "absolute", width: "100%", height: "100%" }}>
<Viewer />
diff --git a/src/new_fields/CursorField.ts b/src/fields/CursorField.ts
index 28467377b..28467377b 100644
--- a/src/new_fields/CursorField.ts
+++ b/src/fields/CursorField.ts
diff --git a/src/new_fields/DateField.ts b/src/fields/DateField.ts
index a925148c2..a925148c2 100644
--- a/src/new_fields/DateField.ts
+++ b/src/fields/DateField.ts
diff --git a/src/new_fields/Doc.ts b/src/fields/Doc.ts
index 921891799..0a003e4f2 100644
--- a/src/new_fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -93,13 +93,44 @@ export const WidthSym = Symbol("Width");
export const HeightSym = Symbol("Height");
export const DataSym = Symbol("Data");
export const LayoutSym = Symbol("Layout");
+export const AclSym = Symbol("Acl");
+export const AclPrivate = Symbol("AclOwnerOnly");
+export const AclReadonly = Symbol("AclReadOnly");
+export const AclAddonly = Symbol("AclAddonly");
export const UpdatingFromServer = Symbol("UpdatingFromServer");
const CachedUpdates = Symbol("Cached updates");
function fetchProto(doc: Doc) {
+ if (doc.author !== Doc.CurrentUserEmail) {
+ const acl = Doc.Get(doc, "ACL", true);
+ switch (acl) {
+ case "ownerOnly":
+ doc[AclSym] = AclPrivate;
+ return undefined;
+ case "readOnly":
+ doc[AclSym] = AclReadonly;
+ break;
+ case "addOnly":
+ doc[AclSym] = AclAddonly;
+ break;
+ }
+ }
+
const proto = doc.proto;
if (proto instanceof Promise) {
+ proto.then(proto => {
+ if (proto.author !== Doc.CurrentUserEmail) {
+ if (proto.ACL === "ownerOnly") {
+ proto[AclSym] = doc[AclSym] = AclPrivate;
+ return undefined;
+ } else if (proto.ACL === "readOnly") {
+ proto[AclSym] = doc[AclSym] = AclReadonly;
+ } else if (proto.ACL === "addOnly") {
+ proto[AclSym] = doc[AclSym] = AclAddonly;
+ }
+ }
+ });
return proto;
}
}
@@ -113,10 +144,10 @@ export class Doc extends RefField {
set: setter,
get: getter,
// getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter
- has: (target, key) => key in target.__fields,
+ has: (target, key) => target[AclSym] !== AclPrivate && key in target.__fields,
ownKeys: target => {
const obj = {} as any;
- Object.assign(obj, target.___fields);
+ (target[AclSym] !== AclPrivate) && Object.assign(obj, target.___fields);
runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
return Object.keys(obj);
},
@@ -170,8 +201,11 @@ export class Doc extends RefField {
private [Self] = this;
private [SelfProxy]: any;
+ public [AclSym]: any = undefined;
public [WidthSym] = () => NumCast(this[SelfProxy]._width);
public [HeightSym] = () => NumCast(this[SelfProxy]._height);
+ public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; }
+ public [ToString]() { return `Doc(${this[AclSym] === AclPrivate ? "-inaccessible-" : this.title})`; }
public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
public get [DataSym]() {
const self = this[SelfProxy];
@@ -193,8 +227,6 @@ export class Doc extends RefField {
return undefined;
}
- [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; }
- [ToString]() { return `Doc(${this.title})`; }
private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
public static CurrentUserEmail: string = "";
@@ -456,6 +488,7 @@ export namespace Doc {
}
alias.aliasOf = doc;
alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`);
+ alias.author = Doc.CurrentUserEmail;
return alias;
}
@@ -574,7 +607,7 @@ export namespace Doc {
if (field instanceof RefField) {
copy[key] = field;
} else if (cfield instanceof ComputedField) {
- copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
+ copy[key] = cfield[Copy]();// ComputedField.MakeFunction(cfield.script.originalScript);
} else if (field instanceof ObjectField) {
copy[key] = doc[key] instanceof Doc ?
key.includes("layout[") ? Doc.MakeCopy(doc[key] as Doc, false) : doc[key] : // reference documents except copy documents that are expanded teplate fields
@@ -586,7 +619,7 @@ export namespace Doc {
}
}
});
-
+ copy["author"] = Doc.CurrentUserEmail;
return copy;
}
@@ -794,6 +827,7 @@ export namespace Doc {
}
// don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
export function IsBrushedDegreeUnmemoized(doc: Doc) {
+ if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return 0;
return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
}
export function IsBrushedDegree(doc: Doc) {
@@ -802,11 +836,13 @@ export namespace Doc {
})(doc);
}
export function BrushDoc(doc: Doc) {
+ if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc;
brushManager.BrushedDoc.set(doc, true);
brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
return doc;
}
export function UnBrushDoc(doc: Doc) {
+ if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc;
brushManager.BrushedDoc.delete(doc);
brushManager.BrushedDoc.delete(Doc.GetProto(doc));
return doc;
@@ -836,6 +872,7 @@ export namespace Doc {
}
const highlightManager = new HighlightBrush();
export function IsHighlighted(doc: Doc) {
+ if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return false;
return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc));
}
export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) {
@@ -1028,28 +1065,32 @@ export namespace Doc {
if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
}
- export function pileup(selected: Doc[], x: number, y: number) {
- const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
+ export function pileup(docList: Doc[], x?: number, y?: number) {
let w = 0, h = 0;
- selected.forEach((d, i) => {
- Doc.iconify(d);
- w = Math.max(d[WidthSym](), w);
- h = Math.max(d[HeightSym](), h);
- });
- h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away. so this is a fallback hack to try to do something reasonable
- selected.forEach((d, i) => {
- d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2;
- d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2;
- d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ runInAction(() => {
+ docList.forEach(d => {
+ Doc.iconify(d);
+ w = Math.max(d[WidthSym](), w);
+ h = Math.max(d[HeightSym](), h);
+ });
+ h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away. so this is a fallback hack to try to do something reasonable
+ docList.forEach((d, i) => {
+ d.x = Math.cos(Math.PI * 2 * i / docList.length) * 10 - w / 2;
+ d.y = Math.sin(Math.PI * 2 * i / docList.length) * 10 - h / 2;
+ d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ });
});
- newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
- newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
- newCollection._width = newCollection._height = 110;
- //newCollection.borderRounding = "40px";
- newCollection._jitterRotation = 10;
- newCollection._backgroundColor = "gray";
- newCollection._overflow = "visible";
- return newCollection;
+ if (x !== undefined && y !== undefined) {
+ const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
+ newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
+ newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
+ newCollection._width = newCollection._height = 110;
+ //newCollection.borderRounding = "40px";
+ newCollection._jitterRotation = 10;
+ newCollection._backgroundColor = "gray";
+ newCollection._overflow = "visible";
+ return newCollection;
+ }
}
diff --git a/src/new_fields/FieldSymbols.ts b/src/fields/FieldSymbols.ts
index 8d040f493..8d040f493 100644
--- a/src/new_fields/FieldSymbols.ts
+++ b/src/fields/FieldSymbols.ts
diff --git a/src/new_fields/HtmlField.ts b/src/fields/HtmlField.ts
index 6e8bba977..6e8bba977 100644
--- a/src/new_fields/HtmlField.ts
+++ b/src/fields/HtmlField.ts
diff --git a/src/new_fields/IconField.ts b/src/fields/IconField.ts
index 76c4ddf1b..76c4ddf1b 100644
--- a/src/new_fields/IconField.ts
+++ b/src/fields/IconField.ts
diff --git a/src/new_fields/InkField.ts b/src/fields/InkField.ts
index bb93de5ac..bb93de5ac 100644
--- a/src/new_fields/InkField.ts
+++ b/src/fields/InkField.ts
diff --git a/src/new_fields/List.ts b/src/fields/List.ts
index fdabea365..fdabea365 100644
--- a/src/new_fields/List.ts
+++ b/src/fields/List.ts
diff --git a/src/new_fields/ListSpec.ts b/src/fields/ListSpec.ts
index e69de29bb..e69de29bb 100644
--- a/src/new_fields/ListSpec.ts
+++ b/src/fields/ListSpec.ts
diff --git a/src/new_fields/ObjectField.ts b/src/fields/ObjectField.ts
index 9aa1c9b04..9aa1c9b04 100644
--- a/src/new_fields/ObjectField.ts
+++ b/src/fields/ObjectField.ts
diff --git a/src/new_fields/PresField.ts b/src/fields/PresField.ts
index f236a04fd..f236a04fd 100644
--- a/src/new_fields/PresField.ts
+++ b/src/fields/PresField.ts
diff --git a/src/new_fields/Proxy.ts b/src/fields/Proxy.ts
index 555faaad0..555faaad0 100644
--- a/src/new_fields/Proxy.ts
+++ b/src/fields/Proxy.ts
diff --git a/src/new_fields/RefField.ts b/src/fields/RefField.ts
index b6ef69750..b6ef69750 100644
--- a/src/new_fields/RefField.ts
+++ b/src/fields/RefField.ts
diff --git a/src/new_fields/RichTextField.ts b/src/fields/RichTextField.ts
index 5cf0e0cc3..5cf0e0cc3 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
diff --git a/src/new_fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index c475d0d73..c475d0d73 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
diff --git a/src/new_fields/Schema.ts b/src/fields/Schema.ts
index 72bce283d..72bce283d 100644
--- a/src/new_fields/Schema.ts
+++ b/src/fields/Schema.ts
diff --git a/src/new_fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index 07c90f5a2..07c90f5a2 100644
--- a/src/new_fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
diff --git a/src/new_fields/ScriptField.ts b/src/fields/ScriptField.ts
index 8d0ddf94c..fc7f9ca80 100644
--- a/src/new_fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -1,10 +1,10 @@
import { ObjectField } from "./ObjectField";
-import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions } from "../client/util/Scripting";
+import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions, CompileError, CompileResult, Scripting } from "../client/util/Scripting";
import { Copy, ToScriptString, ToString, Parent, SelfProxy } from "./FieldSymbols";
import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr";
import { Deserializable, autoObject } from "../client/util/SerializationHelper";
-import { Doc, Field } from "../new_fields/Doc";
-import { Plugins } from "./util";
+import { Doc, Field } from "./Doc";
+import { Plugins, setter } from "./util";
import { computedFn } from "mobx-utils";
import { ProxyField } from "./Proxy";
import { Cast } from "./Types";
@@ -59,18 +59,20 @@ async function deserializeScript(script: ScriptField) {
export class ScriptField extends ObjectField {
@serializable(object(scriptSchema))
readonly script: CompiledScript;
+ @serializable(object(scriptSchema))
+ readonly setterscript: CompiledScript | undefined;
@serializable(autoObject())
private captures?: ProxyField<Doc>;
- constructor(script: CompiledScript) {
+ constructor(script: CompiledScript, setterscript?: CompiledScript) {
super();
- if (script && script.options.capturedVariables) {
+ if (script?.options.capturedVariables) {
const doc = Doc.assign(new Doc, script.options.capturedVariables);
this.captures = new ProxyField(doc);
}
-
+ this.setterscript = setterscript;
this.script = script;
}
@@ -96,10 +98,10 @@ export class ScriptField extends ObjectField {
// }
[Copy](): ObjectField {
- return new ScriptField(this.script);
+ return new ScriptField(this.script, this.setterscript);
}
toString() {
- return `${this.script.originalScript}`;
+ return `${this.script.originalScript} + ${this.setterscript?.originalScript}`;
}
[ToScriptString]() {
@@ -136,6 +138,12 @@ export class ComputedField extends ScriptField {
//TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
value = computedFn((doc: Doc) => this._valueOutsideReaction(doc));
_valueOutsideReaction = (doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult }, console.log).result;
+
+
+ [Copy](): ObjectField {
+ return new ComputedField(this.script, this.setterscript);
+ }
+
public static MakeScript(script: string, params: object = {}) {
const compiled = ScriptField.CompileScript(script, params, false);
return compiled.compiled ? new ComputedField(compiled) : undefined;
@@ -144,8 +152,17 @@ export class ComputedField extends ScriptField {
const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
return compiled.compiled ? new ComputedField(compiled) : undefined;
}
+ public static MakeInterpolated(fieldKey: string, interpolatorKey: string) {
+ const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {});
+ const setField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}] = value`, { value: "any" }, true, {});
+ return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
+ }
}
+Scripting.addGlobal(function getIndexVal(list: any[], index: number) {
+ return list.reduce((p, x, i) => (i <= index && x !== undefined) || p === undefined ? x : p, undefined as any);
+});
+
export namespace ComputedField {
let useComputed = true;
export function DisableComputedFields() {
diff --git a/src/new_fields/Types.ts b/src/fields/Types.ts
index 3d784448d..3d784448d 100644
--- a/src/new_fields/Types.ts
+++ b/src/fields/Types.ts
diff --git a/src/new_fields/URLField.ts b/src/fields/URLField.ts
index fb71160ca..fb71160ca 100644
--- a/src/new_fields/URLField.ts
+++ b/src/fields/URLField.ts
diff --git a/src/new_fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index cacba43b6..e7031cc39 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -12,6 +12,9 @@ export const documentSchema = createSchema({
links: listSpec(Doc), // computed (readonly) list of links associated with this document
// "Location" properties in a very general sense
+ currentFrame: "number", // current frame of a frame based collection (e.g., a progressive slide)
+ lastFrame: "number", // last frame of a frame based collection (e.g., a progressive slide)
+ activeFrame: "number", // the active frame of a frame based animated document
currentTimecode: "number", // current play back time of a temporal document (video / audio)
displayTimecode: "number", // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently
@@ -20,7 +23,9 @@ export const documentSchema = createSchema({
z: "number", // z "coordinate" - non-zero specifies the overlay layer of a freeformview
zIndex: "number", // zIndex of a document in a freeform view
scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
+ scrollX: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
scrollTop: "number", // scroll position of a scrollable document (pdf, text, web)
+ scrollLeft: "number", // scroll position of a scrollable document (pdf, text, web)
// appearance properties on the layout
_autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents
@@ -74,7 +79,7 @@ export const documentSchema = createSchema({
isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked
isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee)
lockedPosition: "boolean", // whether the document can be moved (dragged)
- lockedTransform: "boolean", // whether the document can be panned/zoomed
+ _lockedTransform: "boolean",// whether a freeformview can pan/zoom
// drag drop properties
dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document.
diff --git a/src/new_fields/util.ts b/src/fields/util.ts
index 8c719ccd8..54e7eca28 100644
--- a/src/new_fields/util.ts
+++ b/src/fields/util.ts
@@ -1,5 +1,5 @@
import { UndoManager } from "../client/util/UndoManager";
-import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym } from "./Doc";
+import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym, AclSym, AclPrivate } from "./Doc";
import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField, PrefetchProxy } from "./Proxy";
import { RefField } from "./RefField";
@@ -7,6 +7,8 @@ import { ObjectField } from "./ObjectField";
import { action, trace } from "mobx";
import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
import { DocServer } from "../client/DocServer";
+import { ComputedField } from "./ScriptField";
+import { ScriptCast } from "./Types";
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
@@ -104,6 +106,7 @@ const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHe
"LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
let prop = in_prop;
+ if (target[AclSym]) return true;
if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
if (!prop.startsWith("_")) {
console.log(prop + " is deprecated - switch to _" + prop);
@@ -114,11 +117,16 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
return true;
}
}
+ if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) {
+ return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false;
+ }
return _setter(target, prop, value, receiver);
}
export function getter(target: any, in_prop: string | symbol | number, receiver: any): any {
let prop = in_prop;
+ if (in_prop === AclSym) return target[AclSym];
+ if (target[AclSym] === AclPrivate) return undefined;
if (prop === LayoutSym) {
return target.__LAYOUT__;
}
@@ -143,6 +151,9 @@ export function getter(target: any, in_prop: string | symbol | number, receiver:
function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any {
receiver = receiver || target[SelfProxy];
+ if (target === undefined) {
+ console.log("");
+ }
let field = target.__fields[prop];
for (const plugin of getterPlugins) {
const res = plugin(receiver, prop, field);
@@ -155,7 +166,7 @@ function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreP
}
if (field === undefined && !ignoreProto && prop !== "proto") {
const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters
- if (proto instanceof Doc) {
+ if (proto instanceof Doc && proto[AclSym] !== AclPrivate) {
return getFieldImpl(proto[Self], prop, receiver, ignoreProto);
}
return undefined;
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index 295e82142..b15042f9f 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -4,19 +4,16 @@ import { Docs } from '../client/documents/Documents';
import "./ImageUpload.scss";
import React = require('react');
import { DocServer } from '../client/DocServer';
-import { Opt, Doc } from '../new_fields/Doc';
-import { Cast } from '../new_fields/Types';
-import { listSpec } from '../new_fields/Schema';
-import { List } from '../new_fields/List';
+import { Opt, Doc } from '../fields/Doc';
+import { Cast } from '../fields/Types';
+import { listSpec } from '../fields/Schema';
+import { List } from '../fields/List';
import { observer } from 'mobx-react';
import { observable } from 'mobx';
import { Utils } from '../Utils';
import MobileInterface from './MobileInterface';
-import { CurrentUserUtils } from '../server/authentication/models/current_user_utils';
-import { Scripting } from '../client/util/Scripting';
-
-
-
+import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
+import { resolvedPorts } from '../client/views/Main';
// const onPointerDown = (e: React.TouchEvent) => {
// let imgInput = document.getElementById("input_image_file");
@@ -107,10 +104,10 @@ class Uploader extends React.Component {
}
-// DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload");
+// DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, "image upload");
(async () => {
const info = await CurrentUserUtils.loadCurrentUser();
- DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + "mobile");
+ DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, info.email + "mobile");
await Docs.Prototypes.initialize();
if (info.id !== "__guest__") {
// a guest will not have an id registered
diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx
index 1537ae034..973931615 100644
--- a/src/mobile/MobileInkOverlay.tsx
+++ b/src/mobile/MobileInkOverlay.tsx
@@ -4,11 +4,11 @@ import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPosition
import { observable, action } from "mobx";
import { GestureUtils } from "../pen-gestures/GestureUtils";
import "./MobileInkOverlay.scss";
-import { StrCast, Cast } from '../new_fields/Types';
+import { StrCast, Cast } from '../fields/Types';
import { DragManager } from "../client/util/DragManager";
import { DocServer } from '../client/DocServer';
-import { Doc, DocListCastAsync } from '../new_fields/Doc';
-import { listSpec } from '../new_fields/Schema';
+import { Doc, DocListCastAsync } from '../fields/Doc';
+import { listSpec } from '../fields/Schema';
@observer
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 69a80e1b4..6c2e797d6 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -10,22 +10,22 @@ import { DocumentManager } from '../client/util/DocumentManager';
import RichTextMenu from '../client/views/nodes/formattedText/RichTextMenu';
import { Scripting } from '../client/util/Scripting';
import { Transform } from '../client/util/Transform';
-import { CollectionView } from '../client/views/collections/CollectionView';
import { DocumentDecorations } from '../client/views/DocumentDecorations';
import GestureOverlay from '../client/views/GestureOverlay';
import { InkingControl } from '../client/views/InkingControl';
import { DocumentView } from '../client/views/nodes/DocumentView';
import { RadialMenu } from '../client/views/nodes/RadialMenu';
import { PreviewCursor } from '../client/views/PreviewCursor';
-import { Doc, DocListCast, FieldResult } from '../new_fields/Doc';
-import { Id } from '../new_fields/FieldSymbols';
-import { InkTool } from '../new_fields/InkField';
-import { listSpec } from '../new_fields/Schema';
-import { Cast, FieldValue } from '../new_fields/Types';
-import { WebField } from "../new_fields/URLField";
-import { CurrentUserUtils } from '../server/authentication/models/current_user_utils';
+import { Doc, DocListCast, FieldResult } from '../fields/Doc';
+import { Id } from '../fields/FieldSymbols';
+import { InkTool } from '../fields/InkField';
+import { listSpec } from '../fields/Schema';
+import { Cast, FieldValue } from '../fields/Types';
+import { WebField } from "../fields/URLField";
+import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from '../Utils';
import "./MobileInterface.scss";
+import { CollectionView } from '../client/views/collections/CollectionView';
library.add(faLongArrowAltLeft);
diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts
index b8a82ab4d..3b6170f68 100644
--- a/src/pen-gestures/GestureUtils.ts
+++ b/src/pen-gestures/GestureUtils.ts
@@ -1,9 +1,9 @@
import { NDollarRecognizer } from "./ndollar";
import { Type } from "typescript";
-import { InkField, PointData } from "../new_fields/InkField";
+import { InkField, PointData } from "../fields/InkField";
import { Docs } from "../client/documents/Documents";
-import { Doc, WidthSym, HeightSym } from "../new_fields/Doc";
-import { NumCast } from "../new_fields/Types";
+import { Doc, WidthSym, HeightSym } from "../fields/Doc";
+import { NumCast } from "../fields/Types";
import { CollectionFreeFormView } from "../client/views/collections/collectionFreeForm/CollectionFreeFormView";
import { Rect } from "react-measure";
import { Scripting } from "../client/util/Scripting";
diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts
index 94302c7b3..684c00c0d 100644
--- a/src/scraping/buxton/final/BuxtonImporter.ts
+++ b/src/scraping/buxton/final/BuxtonImporter.ts
@@ -1,4 +1,4 @@
-import { readdirSync, writeFile, mkdirSync } from "fs";
+import { readdirSync, writeFile, mkdirSync, createReadStream, createWriteStream, existsSync, statSync } from "fs";
import * as path from "path";
import { red, cyan, yellow } from "colors";
import { Utils } from "../../../Utils";
@@ -9,6 +9,7 @@ const createImageSizeStream = require("image-size-stream");
import { parseXml } from "libxmljs";
import { strictEqual } from "assert";
import { Readable, PassThrough } from "stream";
+import { Directory, serverPathToFile, pathToDirectory } from "../../../server/ApiManagers/UploadManager";
/**
* This is an arbitrary bundle of data that gets populated
@@ -18,8 +19,7 @@ interface DocumentContents {
body: string;
imageData: ImageData[];
hyperlinks: string[];
- captions: string[];
- embeddedFileNames: string[];
+ tableData: TableData[];
longDescription: string;
}
@@ -40,6 +40,7 @@ export interface DeviceDocument {
secondaryKey: string;
attribute: string;
__images: ImageData[];
+ additionalMedia: ({ [type: string]: string } | undefined)[];
hyperlinks: string[];
captions: string[]; // from the table column
embeddedFileNames: string[]; // from the table column
@@ -255,6 +256,8 @@ const FormatMap = new Map<keyof DeviceDocument, ValueFormatDefinition<any>>([
]);
const sourceDir = path.resolve(__dirname, "source"); // where the Word documents are assumed to be stored
+const assetDir = path.resolve(__dirname, "assets"); // where any additional media content like pdfs will be stored. Each subdirectory of this
+// must follow the enum Directory.<type> naming scheme
const outDir = path.resolve(__dirname, "json"); // where the JSON output of these device documents will be written
const imageDir = path.resolve(__dirname, "../../../server/public/files/images/buxton"); // where, in the server, these images will be written
const successOut = "buxton.json"; // the JSON list representing properly formatted documents
@@ -277,12 +280,13 @@ export default async function executeImport(emitter: ResultCallback, terminator:
rimraf.sync(dir);
mkdirSync(dir);
});
+ await transferAssets();
return parseFiles(wordDocuments, emitter, terminator);
} catch (e) {
const message = [
"Unable to find a source directory.",
- "Please ensure that the following directory exists and is populated with Word documents:",
- `${sourceDir}`
+ "Please ensure that the following directory exists:",
+ `${e.message}`
].join('\n');
console.log(red(message));
return { error: message };
@@ -290,6 +294,32 @@ export default async function executeImport(emitter: ResultCallback, terminator:
}
/**
+ * Builds a mirrored directory structure of all media / asset files
+ * within the server's public directory.
+ */
+async function transferAssets() {
+ for (const assetType of readdirSync(assetDir)) {
+ const subroot = path.resolve(assetDir, assetType);
+ if (!statSync(subroot).isDirectory()) {
+ continue;
+ }
+ const outputSubroot = serverPathToFile(assetType as Directory, "buxton");
+ if (existsSync(outputSubroot)) {
+ continue;
+ } else {
+ mkdirSync(outputSubroot);
+ }
+ for (const fileName of readdirSync(subroot)) {
+ const readStream = createReadStream(path.resolve(subroot, fileName));
+ const writeStream = createWriteStream(path.resolve(outputSubroot, fileName));
+ await new Promise<void>(resolve => {
+ readStream.pipe(writeStream).on("close", resolve);
+ });
+ }
+ }
+}
+
+/**
* Parse every Word document in the directory, notifying any callers as needed
* at each iteration via the emitter.
* @param wordDocuments the string list of Word document names to parse
@@ -356,6 +386,16 @@ const xPaths = {
hyperlinks: '//*[name()="Relationship" and contains(@Type, "hyperlink")]'
};
+interface TableData {
+ fileName: string;
+ caption: string;
+ additionalMedia?: { [type: string]: string };
+}
+
+const SuffixDirectoryMap = new Map<string, Directory>([
+ ["p", Directory.pdfs]
+]);
+
/**
* The meat of the script, images and text content are extracted here
* @param pathToDocument the path to the document relative to the root of the zip
@@ -370,8 +410,7 @@ async function extractFileContents(pathToDocument: string): Promise<DocumentCont
// get plain text
const body = document.root()?.text() ?? "No body found. Check the import script's XML parser.";
const captions: string[] = [];
- const embeddedFileNames: string[] = [];
-
+ const tableData: TableData[] = [];
// preserve paragraph formatting and line breaks that would otherwise get lost in the plain text parsing
// of the XML hierarchy
const paragraphs = document.find(xPaths.paragraphs).map(node => Utilities.correctSentences(node.text()).transformed!);
@@ -382,7 +421,7 @@ async function extractFileContents(pathToDocument: string): Promise<DocumentCont
// extract captions from the table cells
const tableRowsFlattened = document.find(xPaths.tableCells).map(node => node.text().trim());
const { length } = tableRowsFlattened;
- const numCols = 3;
+ const numCols = 4;
strictEqual(length > numCols, true, "No captions written."); // first row has the headers, not content
strictEqual(length % numCols === 0, true, "Improper caption formatting.");
@@ -392,8 +431,14 @@ async function extractFileContents(pathToDocument: string): Promise<DocumentCont
// have been added or reordered since this was written, but follow the same appraoch)
for (let i = numCols; i < tableRowsFlattened.length; i += numCols) {
const row = tableRowsFlattened.slice(i, i + numCols);
- embeddedFileNames.push(row[1]);
- captions.push(row[2]);
+ const entry: TableData = { fileName: row[1], caption: row[2] };
+ const key = SuffixDirectoryMap.get(row[3].toLowerCase());
+ if (key) {
+ const media: any = {};
+ media[key] = `${entry.fileName.split(".")[0]}.pdf`;
+ entry.additionalMedia = media;
+ }
+ tableData.push(entry);
}
// extract all hyperlinks embedded in the document
@@ -409,7 +454,7 @@ async function extractFileContents(pathToDocument: string): Promise<DocumentCont
// cleanup
zip.close();
- return { body, longDescription, imageData, captions, embeddedFileNames, hyperlinks };
+ return { body, longDescription, imageData, tableData, hyperlinks };
}
// zip relative path from root expression / filter used to isolate only media assets
@@ -451,11 +496,23 @@ async function writeImages(zip: any): Promise<ImageData[]> {
});
// if it's not an icon, by this rough heuristic, i.e. is it not square
- if (Math.abs(width - height) > 10) {
- valid.push({ width, height, type, mediaPath });
+ const number = Number(/image(\d+)/.exec(mediaPath)![1]);
+ if (number > 5 || width - height > 10) {
+ valid.push({ width, height, type, mediaPath, number });
}
}
+ valid.sort((a, b) => a.number - b.number);
+
+ const [{ width: first_w, height: first_h }, { width: second_w, height: second_h }] = valid;
+ if (Math.abs(first_w / second_w - first_h / second_h) < 0.01) {
+ const first_size = first_w * first_h;
+ const second_size = second_w * second_h;
+ const target = first_size >= second_size ? 1 : 0;
+ valid.splice(target, 1);
+ console.log(`Heuristically removed image with size ${target ? second_size : first_size}`);
+ }
+
// for each valid image, output the _o, _l, _m, and _s files
// THIS IS WHERE THE SCRIPT SPENDS MOST OF ITS TIME
for (const { type, width, height, mediaPath } of valid) {
@@ -480,11 +537,12 @@ async function writeImages(zip: any): Promise<ImageData[]> {
* @param contents the data already computed / parsed by extractFileContents
*/
function analyze(fileName: string, contents: DocumentContents): AnalysisResult {
- const { body, imageData, captions, hyperlinks, embeddedFileNames, longDescription } = contents;
+ const { body, imageData, hyperlinks, tableData, longDescription } = contents;
const device: any = {
hyperlinks,
- captions,
- embeddedFileNames,
+ captions: tableData.map(({ caption }) => caption),
+ embeddedFileNames: tableData.map(({ fileName }) => fileName),
+ additionalMedia: tableData.map(({ additionalMedia }) => additionalMedia),
longDescription,
__images: imageData
};
diff --git a/src/scraping/buxton/final/assets/pdfs/3DCad_Brochure.pdf b/src/scraping/buxton/final/assets/pdfs/3DCad_Brochure.pdf
new file mode 100644
index 000000000..4746d2f41
--- /dev/null
+++ b/src/scraping/buxton/final/assets/pdfs/3DCad_Brochure.pdf
Binary files differ
diff --git a/src/server/ApiManagers/ApiManager.ts b/src/server/ApiManagers/ApiManager.ts
index e2b01d585..27e9de065 100644
--- a/src/server/ApiManagers/ApiManager.ts
+++ b/src/server/ApiManagers/ApiManager.ts
@@ -1,4 +1,4 @@
-import RouteManager, { RouteInitializer } from "../RouteManager";
+import { RouteInitializer } from "../RouteManager";
export type Registration = (initializer: RouteInitializer) => void;
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index bd80d6500..46c0d8a8a 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -1,6 +1,6 @@
import ApiManager, { Registration } from "./ApiManager";
import { Method, _permission_denied } from "../RouteManager";
-import { WebSocket } from "../Websocket/Websocket";
+import { WebSocket } from "../websocket";
import { Database } from "../database";
import rimraf = require("rimraf");
import { filesDirectory } from "..";
@@ -14,24 +14,20 @@ export default class DeleteManager extends ApiManager {
register({
method: Method.GET,
+ requireAdminInRelease: true,
subscription: new RouteSubscriber("delete").add("target?"),
- secureHandler: async ({ req, res, isRelease }) => {
- if (isRelease) {
- return _permission_denied(res, "Cannot perform a delete operation outside of the development environment!");
- }
-
+ secureHandler: async ({ req, res }) => {
const { target } = req.params;
- const { doDelete } = WebSocket;
if (!target) {
- await doDelete();
+ await WebSocket.doDelete();
} else {
let all = false;
switch (target) {
case "all":
all = true;
case "database":
- await doDelete(false);
+ await WebSocket.doDelete(false);
if (!all) break;
case "files":
rimraf.sync(filesDirectory);
diff --git a/src/server/ApiManagers/DownloadManager.ts b/src/server/ApiManagers/DownloadManager.ts
index 01d2dfcad..c5f3ca717 100644
--- a/src/server/ApiManagers/DownloadManager.ts
+++ b/src/server/ApiManagers/DownloadManager.ts
@@ -246,7 +246,7 @@ async function writeHierarchyRecursive(file: Archiver.Archiver, hierarchy: Hiera
if (typeof result === "string") {
let path: string;
let matches: RegExpExecArray | null;
- if ((matches = /\:1050\/files\/images\/(upload\_[\da-z]{32}.*)/g.exec(result)) !== null) {
+ if ((matches = /\:\d+\/files\/images\/(upload\_[\da-z]{32}.*)/g.exec(result)) !== null) {
// image already exists on our server
path = serverPathToFile(Directory.images, matches[1]);
} else {
diff --git a/src/server/ApiManagers/GeneralGoogleManager.ts b/src/server/ApiManagers/GeneralGoogleManager.ts
index 17968cc7d..f94b77cac 100644
--- a/src/server/ApiManagers/GeneralGoogleManager.ts
+++ b/src/server/ApiManagers/GeneralGoogleManager.ts
@@ -38,7 +38,7 @@ export default class GeneralGoogleManager extends ApiManager {
method: Method.GET,
subscription: "/revokeGoogleAccessToken",
secureHandler: async ({ user, res }) => {
- await Database.Auxiliary.GoogleAuthenticationToken.Revoke(user.id);
+ await Database.Auxiliary.GoogleAccessToken.Revoke(user.id);
res.send();
}
});
diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts
index 11841a603..be17b698e 100644
--- a/src/server/ApiManagers/GooglePhotosManager.ts
+++ b/src/server/ApiManagers/GooglePhotosManager.ts
@@ -3,7 +3,7 @@ import { Method, _error, _success, _invalid } from "../RouteManager";
import * as path from "path";
import { GoogleApiServerUtils } from "../apis/google/GoogleApiServerUtils";
import { BatchedArray, TimeUnit } from "array-batcher";
-import { Opt } from "../../new_fields/Doc";
+import { Opt } from "../../fields/Doc";
import { DashUploadUtils, InjectSize, SizeSuffix } from "../DashUploadUtils";
import { Database } from "../database";
import { red } from "colors";
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index 0136b758e..d2a9e9cce 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -7,54 +7,54 @@ import { createCanvas } from "canvas";
const imageSize = require("probe-image-size");
import * as express from "express";
import * as path from "path";
-import { Directory, serverPathToFile, clientPathToFile } from "./UploadManager";
+import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from "./UploadManager";
import { red } from "colors";
+import { resolve } from "path";
export default class PDFManager extends ApiManager {
protected initialize(register: Registration): void {
register({
- method: Method.GET,
- subscription: new RouteSubscriber("thumbnail").add("filename"),
- secureHandler: ({ req, res }) => getOrCreateThumbnail(req.params.filename, res)
+ method: Method.POST,
+ subscription: new RouteSubscriber("thumbnail"),
+ secureHandler: async ({ req, res }) => {
+ const { coreFilename, pageNum, subtree } = req.body;
+ return getOrCreateThumbnail(coreFilename, pageNum, res, subtree);
+ }
});
}
}
-async function getOrCreateThumbnail(thumbnailName: string, res: express.Response): Promise<void> {
- const noExtension = thumbnailName.substring(0, thumbnailName.length - ".png".length);
- const pageString = noExtension.split('-')[1];
- const pageNumber = parseInt(pageString);
+async function getOrCreateThumbnail(coreFilename: string, pageNum: number, res: express.Response, subtree?: string): Promise<void> {
+ const resolved = `${coreFilename}-${pageNum}.png`;
return new Promise<void>(async resolve => {
- const path = serverPathToFile(Directory.pdf_thumbnails, thumbnailName);
+ const path = serverPathToFile(Directory.pdf_thumbnails, resolved);
if (existsSync(path)) {
const existingThumbnail = createReadStream(path);
const { err, viewport } = await new Promise<any>(resolve => {
imageSize(existingThumbnail, (err: any, viewport: any) => resolve({ err, viewport }));
});
if (err) {
- console.log(red(`In PDF thumbnail response, unable to determine dimensions of ${thumbnailName}:`));
+ console.log(red(`In PDF thumbnail response, unable to determine dimensions of ${resolved}:`));
console.log(err);
return;
}
- dispatchThumbnail(res, viewport, thumbnailName);
+ dispatchThumbnail(res, viewport, resolved);
} else {
- const offset = thumbnailName.length - pageString.length - 5;
- const name = thumbnailName.substring(0, offset) + ".pdf";
- const path = serverPathToFile(Directory.pdfs, name);
- await CreateThumbnail(path, pageNumber, res);
+ await CreateThumbnail(coreFilename, pageNum, res, subtree);
}
resolve();
});
}
-async function CreateThumbnail(file: string, pageNumber: number, res: express.Response) {
- const documentProxy = await Pdfjs.getDocument(file).promise;
+async function CreateThumbnail(coreFilename: string, pageNum: number, res: express.Response, subtree?: string) {
+ const sourcePath = resolve(pathToDirectory(Directory.pdfs), `${subtree ?? ""}${coreFilename}.pdf`);
+ const documentProxy = await Pdfjs.getDocument(sourcePath).promise;
const factory = new NodeCanvasFactory();
- const page = await documentProxy.getPage(pageNumber);
+ const page = await documentProxy.getPage(pageNum);
const viewport = page.getViewport(1 as any);
const { canvas, context } = factory.create(viewport.width, viewport.height);
const renderContext = {
@@ -64,14 +64,13 @@ async function CreateThumbnail(file: string, pageNumber: number, res: express.Re
};
await page.render(renderContext).promise;
const pngStream = canvas.createPNGStream();
- const filenames = path.basename(file).split(".");
- const thumbnailName = `${filenames[0]}-${pageNumber}.png`;
- const pngFile = serverPathToFile(Directory.pdf_thumbnails, thumbnailName);
+ const resolved = `${coreFilename}-${pageNum}.png`;
+ const pngFile = serverPathToFile(Directory.pdf_thumbnails, resolved);
const out = createWriteStream(pngFile);
pngStream.pipe(out);
return new Promise<void>((resolve, reject) => {
out.on("finish", () => {
- dispatchThumbnail(res, viewport, thumbnailName);
+ dispatchThumbnail(res, viewport, resolved);
resolve();
});
out.on("error", error => {
diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts
index 6638c50e4..7251e07a1 100644
--- a/src/server/ApiManagers/SearchManager.ts
+++ b/src/server/ApiManagers/SearchManager.ts
@@ -193,11 +193,8 @@ export namespace SolrManager {
if (val === null || val === undefined) {
return;
}
- console.log(val);
const type = val.__type || typeof val;
- console.log(type);
let suffix = suffixMap[type];
- console.log(suffix);
if (!suffix) {
return;
}
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index b185d3b55..3dae963be 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -4,14 +4,17 @@ import * as formidable from 'formidable';
import v4 = require('uuid/v4');
const AdmZip = require('adm-zip');
import { extname, basename, dirname } from 'path';
-import { createReadStream, createWriteStream, unlink } from "fs";
+import { createReadStream, createWriteStream, unlink, writeFile } from "fs";
import { publicDirectory, filesDirectory } from "..";
import { Database } from "../database";
import { DashUploadUtils, InjectSize, SizeSuffix } from "../DashUploadUtils";
import * as sharp from 'sharp';
import { AcceptibleMedia, Upload } from "../SharedMediaTypes";
import { normalize } from "path";
+import RouteSubscriber from "../RouteSubscriber";
const imageDataUri = require('image-data-uri');
+import { isWebUri } from "valid-url";
+import { Opt } from "../../fields/Doc";
export enum Directory {
parsed_files = "parsed_files",
@@ -61,10 +64,38 @@ export default class UploadManager extends ApiManager {
});
register({
- method: Method.GET,
- subscription: "/hello",
- secureHandler: ({ req, res }) => {
- res.send("<h1>world!</h1>");
+ method: Method.POST,
+ subscription: new RouteSubscriber("youtubeScreenshot"),
+ secureHandler: async ({ req, res }) => {
+ const { id, timecode } = req.body;
+ const convert = (raw: string) => {
+ const number = Math.floor(Number(raw));
+ const seconds = number % 60;
+ const minutes = (number - seconds) / 60;
+ return `${minutes}m${seconds}s`;
+ };
+ const suffix = timecode ? `&t=${convert(timecode)}` : ``;
+ const targetUrl = `https://www.youtube.com/watch?v=${id}${suffix}`;
+ const buffer = await captureYoutubeScreenshot(targetUrl);
+ if (!buffer) {
+ return res.send();
+ }
+ const resolvedName = `youtube_capture_${id}_${suffix}.png`;
+ const resolvedPath = serverPathToFile(Directory.images, resolvedName);
+ return new Promise<void>(resolve => {
+ writeFile(resolvedPath, buffer, async error => {
+ if (error) {
+ return res.send();
+ }
+ await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images));
+ res.send({
+ accessPaths: {
+ agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName)
+ }
+ } as Upload.FileInformation);
+ resolve();
+ });
+ });
}
});
@@ -244,4 +275,37 @@ export default class UploadManager extends ApiManager {
}
+}
+function delay(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+/**
+ * On success, returns a buffer containing the bytes of a screenshot
+ * of the video (optionally, at a timecode) specified by @param targetUrl.
+ *
+ * On failure, returns undefined.
+ */
+async function captureYoutubeScreenshot(targetUrl: string){
+ // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
+ // const page = await browser.newPage();
+ // await page.setViewport({ width: 1920, height: 1080 });
+
+ // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
+
+ // const videoPlayer = await page.$('.html5-video-player');
+ // videoPlayer && await page.focus("video");
+ // await delay(7000);
+ // const ad = await page.$('.ytp-ad-skip-button-text');
+ // await ad?.click();
+ // await videoPlayer?.click();
+ // await delay(1000);
+ // // hide youtube player controls.
+ // await page.evaluate(() =>
+ // (document.querySelector('.ytp-chrome-bottom') as any).style.display = 'none');
+
+ // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
+ // await browser.close();
+
+ // return buffer;
+ return null;
} \ No newline at end of file
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 68b3107ae..0d1d8f218 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -3,7 +3,7 @@ import { Method } from "../RouteManager";
import { Database } from "../database";
import { msToTime } from "../ActionUtilities";
import * as bcrypt from "bcrypt-nodejs";
-import { Opt } from "../../new_fields/Doc";
+import { Opt } from "../../fields/Doc";
export const timeMap: { [id: string]: number } = {};
interface ActivityUnit {
diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts
index aec523cd0..e2cd88726 100644
--- a/src/server/ApiManagers/UtilManager.ts
+++ b/src/server/ApiManagers/UtilManager.ts
@@ -1,8 +1,6 @@
import ApiManager, { Registration } from "./ApiManager";
import { Method } from "../RouteManager";
import { exec } from 'child_process';
-import RouteSubscriber from "../RouteSubscriber";
-import { red } from "colors";
// import { IBM_Recommender } from "../../client/apis/IBM_Recommender";
// import { Recommender } from "../Recommender";
@@ -34,7 +32,6 @@ export default class UtilManager extends ApiManager {
// }
// });
-
register({
method: Method.GET,
subscription: "/pull",
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index ef9b88541..ab3dfffcc 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -2,7 +2,7 @@ import { Email, pathFromRoot } from "../ActionUtilities";
import { red, yellow, green, cyan } from "colors";
import { get } from "request-promise";
import { Utils } from "../../Utils";
-import { WebSocket } from "../Websocket/Websocket";
+import { WebSocket } from "../websocket";
import { MessageStore } from "../Message";
import { launchServer, onWindows } from "..";
import { readdirSync, statSync, createWriteStream, readFileSync, unlinkSync } from "fs";
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 8567631cd..2bf4c1956 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -4,7 +4,7 @@ import * as path from 'path';
import * as sharp from 'sharp';
import request = require('request-promise');
import { ExifImage } from 'exif';
-import { Opt } from '../new_fields/Doc';
+import { Opt } from '../fields/Doc';
import { AcceptibleMedia, Upload } from './SharedMediaTypes';
import { filesDirectory, publicDirectory } from '.';
import { File } from 'formidable';
@@ -15,7 +15,9 @@ const parse = require('pdf-parse');
import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from './ApiManagers/UploadManager';
import { red } from 'colors';
import { Stream } from 'stream';
+import { resolvedPorts } from './server_Initialization';
const requestImageSize = require("../client/util/request-image-size");
+import { resolvedServerUrl } from "./server_Initialization";
export enum SizeSuffix {
Small = "_s",
@@ -184,7 +186,7 @@ export namespace DashUploadUtils {
if (error !== null) {
return error;
}
- source = `http://localhost:1050${clientPathToFile(Directory.images, resolved)}`;
+ source = `${resolvedServerUrl}${clientPathToFile(Directory.images, resolved)}`;
}
let resolvedUrl: string;
/**
@@ -194,14 +196,14 @@ export namespace DashUploadUtils {
* basename subtree (i.e. /images/<some_guid>.<ext>) and put it on the end of the server's url.
*
* This can always be localhost, regardless of whether this is on the server or not, since we (the server, not the client)
- * will be the ones making the request, and from the perspective of dash-release or dash-web, localhost:1050 refers to the same thing
- * as the full dash-release.eastus.cloudapp.azure.com:1050.
+ * will be the ones making the request, and from the perspective of dash-release or dash-web, localhost:<port> refers to the same thing
+ * as the full dash-release.eastus.cloudapp.azure.com:<port>.
*/
const matches = isLocal().exec(source);
if (matches === null) {
resolvedUrl = source;
} else {
- resolvedUrl = `http://localhost:1050/${matches[1].split("\\").join("/")}`;
+ resolvedUrl = `${resolvedServerUrl}/${matches[1].split("\\").join("/")}`;
}
// See header comments: not all image files have exif data (I believe only JPG is the only format that can have it)
const exifData = await parseExifData(resolvedUrl);
@@ -257,7 +259,7 @@ export namespace DashUploadUtils {
});
}
- function getAccessPaths(directory: Directory, fileName: string) {
+ export function getAccessPaths(directory: Directory, fileName: string) {
return {
client: clientPathToFile(directory, fileName),
server: serverPathToFile(directory, fileName)
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 01aae5de7..80f372733 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -1,6 +1,6 @@
import { Utils } from "../Utils";
import { Point } from "../pen-gestures/ndollar";
-import { Doc } from "../new_fields/Doc";
+import { Doc } from "../fields/Doc";
import { Image } from "canvas";
import { AnalysisResult, ImportResults } from "../scraping/buxton/final/BuxtonImporter";
diff --git a/src/server/Recommender.ts b/src/server/Recommender.ts
index aacdb4053..423ce9b46 100644
--- a/src/server/Recommender.ts
+++ b/src/server/Recommender.ts
@@ -1,6 +1,6 @@
-// //import { Doc } from "../new_fields/Doc";
-// //import { StrCast } from "../new_fields/Types";
-// //import { List } from "../new_fields/List";
+// //import { Doc } from "../fields/Doc";
+// //import { StrCast } from "../fields/Types";
+// //import { List } from "../fields/List";
// //import { CognitiveServices } from "../client/cognitive_services/CognitiveServices";
// // var w2v = require('word2vec');
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 80e4a6741..1a2340afc 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,7 +1,8 @@
import RouteSubscriber from "./RouteSubscriber";
-import { DashUserModel } from "./authentication/models/user_model";
+import { DashUserModel } from "./authentication/DashUserModel";
import { Request, Response, Express } from 'express';
import { cyan, red, green } from 'colors';
+import { AdminPriviliges } from ".";
export enum Method {
GET,
@@ -25,6 +26,7 @@ export interface RouteInitializer {
secureHandler: SecureHandler;
publicHandler?: PublicHandler;
errorHandler?: ErrorHandler;
+ requireAdminInRelease?: true;
}
const registered = new Map<string, Set<Method>>();
@@ -84,7 +86,7 @@ export default class RouteManager {
* @param initializer
*/
addSupervisedRoute = (initializer: RouteInitializer): void => {
- const { method, subscription, secureHandler, publicHandler, errorHandler } = initializer;
+ const { method, subscription, secureHandler, publicHandler, errorHandler, requireAdminInRelease: requireAdmin } = initializer;
typeof (initializer.subscription) === "string" && RouteManager.routes.push(initializer.subscription);
initializer.subscription instanceof RouteSubscriber && RouteManager.routes.push(initializer.subscription.root);
@@ -94,7 +96,7 @@ export default class RouteManager {
});
const isRelease = this._isRelease;
const supervised = async (req: Request, res: Response) => {
- let { user } = req;
+ let user = req.user as Partial<DashUserModel> | undefined;
const { originalUrl: target } = req;
if (process.env.DB === "MEM" && !user) {
user = { id: "guest", email: "", userDocumentId: "guestDocId" };
@@ -113,6 +115,13 @@ export default class RouteManager {
}
};
if (user) {
+ if (requireAdmin && isRelease && process.env.PASSWORD) {
+ if (AdminPriviliges.get(user.id)) {
+ AdminPriviliges.delete(user.id);
+ } else {
+ return res.redirect(`/admin/${req.originalUrl.substring(1).replace("/", ":")}`);
+ }
+ }
await tryExecute(secureHandler, { ...core, user });
} else {
req.session!.target = target;
@@ -205,5 +214,5 @@ export function _permission_denied(res: Response, message?: string) {
if (message) {
res.statusMessage = message;
}
- res.status(STATUS.PERMISSION_DENIED).send("Permission Denied!");
+ res.status(STATUS.PERMISSION_DENIED).send(`Permission Denied! ${message}`);
}
diff --git a/src/server/apis/google/CredentialsLoader.ts b/src/server/apis/google/CredentialsLoader.ts
new file mode 100644
index 000000000..ef1f9a91e
--- /dev/null
+++ b/src/server/apis/google/CredentialsLoader.ts
@@ -0,0 +1,67 @@
+import { readFile, readFileSync } from "fs";
+import { pathFromRoot } from "../../ActionUtilities";
+import { SecureContextOptions } from "tls";
+import { blue, red } from "colors";
+
+export namespace GoogleCredentialsLoader {
+
+ export interface InstalledCredentials {
+ client_id: string;
+ project_id: string;
+ auth_uri: string;
+ token_uri: string;
+ auth_provider_x509_cert_url: string;
+ client_secret: string;
+ redirect_uris: string[];
+ }
+
+ export let ProjectCredentials: InstalledCredentials;
+
+ export async function loadCredentials() {
+ ProjectCredentials = await new Promise<InstalledCredentials>(resolve => {
+ readFile(__dirname + '/google_project_credentials.json', function processClientSecrets(err, content) {
+ if (err) {
+ console.log('Error loading client secret file: ' + err);
+ return;
+ }
+ resolve(JSON.parse(content.toString()).installed);
+ });
+ });
+ }
+
+}
+
+export namespace SSL {
+
+ export let Credentials: SecureContextOptions = {};
+ export let Loaded = false;
+
+ const suffixes = {
+ privateKey: ".key",
+ certificate: ".crt",
+ caBundle: "-ca.crt"
+ };
+
+ export async function loadCredentials() {
+ const { serverName } = process.env;
+ const cert = (suffix: string) => readFileSync(pathFromRoot(`./${serverName}${suffix}`)).toString();
+ try {
+ Credentials.key = cert(suffixes.privateKey);
+ Credentials.cert = cert(suffixes.certificate);
+ Credentials.ca = cert(suffixes.caBundle);
+ Loaded = true;
+ } catch (e) {
+ Credentials = {};
+ Loaded = false;
+ }
+ }
+
+ export function exit() {
+ console.log(red("Running this server in release mode requires the following SSL credentials in the project root:"));
+ const serverName = process.env.serverName ? process.env.serverName : "{process.env.serverName}";
+ Object.values(suffixes).forEach(suffix => console.log(blue(`${serverName}${suffix}`)));
+ console.log(red("Please ensure these files exist and restart, or run this in development mode."));
+ process.exit(0);
+ }
+
+}
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 48a8da89f..20f96f432 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -1,11 +1,11 @@
import { google } from "googleapis";
import { OAuth2Client, Credentials, OAuth2ClientOptions } from "google-auth-library";
-import { Opt } from "../../../new_fields/Doc";
+import { Opt } from "../../../fields/Doc";
import { GaxiosResponse } from "gaxios";
import request = require('request-promise');
-import * as qs from 'query-string';
+import * as qs from "query-string";
import { Database } from "../../database";
-import { GoogleCredentialsLoader } from "../../credentials/CredentialsLoader";
+import { GoogleCredentialsLoader } from "./CredentialsLoader";
/**
* Scopes give Google users fine granularity of control
@@ -224,7 +224,7 @@ export namespace GoogleApiServerUtils {
});
});
const enriched = injectUserInfo(credentials);
- await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, enriched);
+ await Database.Auxiliary.GoogleAccessToken.Write(userId, enriched);
return enriched;
}
@@ -280,7 +280,7 @@ export namespace GoogleApiServerUtils {
* and a flag indicating whether or not they were refreshed during retrieval
*/
export async function retrieveCredentials(userId: string): Promise<{ credentials: Opt<EnrichedCredentials>, refreshed: boolean }> {
- let credentials = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId);
+ let credentials = await Database.Auxiliary.GoogleAccessToken.Fetch(userId);
let refreshed = false;
if (!credentials) {
return { credentials: undefined, refreshed };
@@ -318,7 +318,7 @@ export namespace GoogleApiServerUtils {
});
// expires_in is in seconds, but we're building the new expiry date in milliseconds
const expiry_date = new Date().getTime() + (expires_in * 1000);
- await Database.Auxiliary.GoogleAuthenticationToken.Update(userId, access_token, expiry_date);
+ await Database.Auxiliary.GoogleAccessToken.Update(userId, access_token, expiry_date);
// update the relevant properties
credentials.access_token = access_token;
credentials.expiry_date = expiry_date;
diff --git a/src/server/credentials/google_project_credentials.json b/src/server/apis/google/google_project_credentials.json
index 955c5a3c1..955c5a3c1 100644
--- a/src/server/credentials/google_project_credentials.json
+++ b/src/server/apis/google/google_project_credentials.json
diff --git a/src/server/apis/youtube/youtubeApiSample.js b/src/server/apis/youtube/youtubeApiSample.js
index 50b3c7b38..d535bd9ff 100644
--- a/src/server/apis/youtube/youtubeApiSample.js
+++ b/src/server/apis/youtube/youtubeApiSample.js
@@ -1,6 +1,8 @@
const fs = require('fs');
const readline = require('readline');
-const { google } = require('googleapis');
+const {
+ google
+} = require('googleapis');
const OAuth2 = google.auth.OAuth2;
@@ -19,21 +21,27 @@ module.exports.readApiKey = (callback) => {
}
callback(content);
});
-}
+};
module.exports.authorizedGetChannel = (apiKey) => {
//this didnt get called
// Authorize a client with the loaded credentials, then call the YouTube API.
authorize(JSON.parse(apiKey), getChannel);
-}
+};
module.exports.authorizedGetVideos = (apiKey, userInput, callBack) => {
- authorize(JSON.parse(apiKey), getVideos, { userInput: userInput, callBack: callBack });
-}
+ authorize(JSON.parse(apiKey), getVideos, {
+ userInput: userInput,
+ callBack: callBack
+ });
+};
module.exports.authorizedGetVideoDetails = (apiKey, videoIds, callBack) => {
- authorize(JSON.parse(apiKey), getVideoDetails, { videoIds: videoIds, callBack: callBack });
-}
+ authorize(JSON.parse(apiKey), getVideoDetails, {
+ videoIds: videoIds,
+ callBack: callBack
+ });
+};
/**
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/AuthenticationManager.ts
index f0086d4ea..00f1fe44e 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -1,13 +1,13 @@
-import { default as User, DashUserModel, AuthToken } from "../models/user_model";
+import { default as User, DashUserModel } from "./DashUserModel";
import { Request, Response, NextFunction } from "express";
import * as passport from "passport";
import { IVerifyOptions } from "passport-local";
-import "../config/passport";
+import "./Passport";
import flash = require("express-flash");
import * as async from 'async';
import * as nodemailer from 'nodemailer';
import c = require("crypto");
-import { Utils } from "../../../Utils";
+import { Utils } from "../../Utils";
import { MailOptions } from "nodemailer/lib/stream-transport";
/**
@@ -111,7 +111,7 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
return res.redirect("/signup");
}
- passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => {
+ passport.authenticate("local", (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
if (err) { next(err); return; }
if (!user) {
return res.redirect("/signup");
diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/DashUserModel.ts
index a0b688328..51d920a8f 100644
--- a/src/server/authentication/models/user_model.ts
+++ b/src/server/authentication/DashUserModel.ts
@@ -58,11 +58,11 @@ userSchema.pre("save", function save(next) {
if (!user.isModified("password")) {
return next();
}
- bcrypt.genSalt(10, (err, salt) => {
+ bcrypt.genSalt(10, (err: any, salt: string) => {
if (err) {
return next(err);
}
- bcrypt.hash(user.password, salt, () => void {}, (err: mongoose.Error, hash) => {
+ bcrypt.hash(user.password, salt, () => void {}, (err: mongoose.Error, hash: string) => {
if (err) {
return next(err);
}
diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/Passport.ts
index 286209b20..9b0069414 100644
--- a/src/server/authentication/config/passport.ts
+++ b/src/server/authentication/Passport.ts
@@ -1,6 +1,6 @@
import * as passport from 'passport';
import * as passportLocal from 'passport-local';
-import { default as User } from '../models/user_model';
+import { default as User } from './DashUserModel';
const LocalStrategy = passportLocal.Strategy;
diff --git a/src/server/credentials/CredentialsLoader.ts b/src/server/credentials/CredentialsLoader.ts
deleted file mode 100644
index e3f4d167b..000000000
--- a/src/server/credentials/CredentialsLoader.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { readFile } from "fs";
-
-export namespace GoogleCredentialsLoader {
-
- export interface InstalledCredentials {
- client_id: string;
- project_id: string;
- auth_uri: string;
- token_uri: string;
- auth_provider_x509_cert_url: string;
- client_secret: string;
- redirect_uris: string[];
- }
-
- export let ProjectCredentials: InstalledCredentials;
-
- export async function loadCredentials() {
- ProjectCredentials = await new Promise<InstalledCredentials>(resolve => {
- readFile(__dirname + '/google_project_credentials.json', function processClientSecrets(err, content) {
- if (err) {
- console.log('Error loading client secret file: ' + err);
- return;
- }
- resolve(JSON.parse(content.toString()).installed);
- });
- });
- }
-
-}
diff --git a/src/server/database.ts b/src/server/database.ts
index 580f7f919..a5f23c4b1 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -1,6 +1,6 @@
import * as mongodb from 'mongodb';
import { Transferable } from './Message';
-import { Opt } from '../new_fields/Doc';
+import { Opt } from '../fields/Doc';
import { Utils, emptyFunction } from '../Utils';
import { Credentials } from 'google-auth-library';
import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils';
@@ -293,13 +293,26 @@ export namespace Database {
export const Instance = getDatabase();
+ /**
+ * Provides definitions and apis for working with
+ * portions of the database not dedicated to storing documents
+ * or Dash-internal user data.
+ */
export namespace Auxiliary {
+ /**
+ * All the auxiliary MongoDB collections (schemas)
+ */
export enum AuxiliaryCollections {
GooglePhotosUploadHistory = "uploadedFromGooglePhotos",
- GoogleAuthentication = "googleAuthentication"
+ GoogleAccess = "googleAuthentication"
}
+ /**
+ * Searches for the @param query in the specified @param collection,
+ * and returns at most the first @param cap results. If @param removeId is true,
+ * as it is by default, each object will be stripped of its database id.
+ */
const SanitizedCappedQuery = async (query: { [key: string]: any }, collection: string, cap: number, removeId = true) => {
const cursor = await Instance.query(query, undefined, collection);
const results = await cursor.toArray();
@@ -310,52 +323,89 @@ export namespace Database {
}) : slice;
};
+ /**
+ * Searches for the @param query in the specified @param collection,
+ * and returns at most the first result. If @param removeId is true,
+ * as it is by default, each object will be stripped of its database id.
+ * Worth the special case since it converts the Array return type to a single
+ * object of the specified type.
+ */
const SanitizedSingletonQuery = async <T>(query: { [key: string]: any }, collection: string, removeId = true): Promise<Opt<T>> => {
const results = await SanitizedCappedQuery(query, collection, 1, removeId);
return results.length ? results[0] : undefined;
};
+ /**
+ * Checks to see if an image with the given @param contentSize
+ * already exists in the aux database, i.e. has already been downloaded from Google Photos.
+ */
export const QueryUploadHistory = async (contentSize: number) => {
return SanitizedSingletonQuery<Upload.ImageInformation>({ contentSize }, AuxiliaryCollections.GooglePhotosUploadHistory);
};
- export namespace GoogleAuthenticationToken {
+ /**
+ * Records the uploading of the image with the given @param information,
+ * using the given content size as a seed for the database id.
+ */
+ export const LogUpload = async (information: Upload.ImageInformation) => {
+ const bundle = {
+ _id: Utils.GenerateDeterministicGuid(String(information.contentSize)),
+ ...information
+ };
+ return Instance.insert(bundle, AuxiliaryCollections.GooglePhotosUploadHistory);
+ };
+
+ /**
+ * Manages the storage, retrieval and updating of the access token that
+ * facilitates interactions with all their APIs for a given account.
+ */
+ export namespace GoogleAccessToken {
+ /**
+ * Format stored in database.
+ */
type StoredCredentials = GoogleApiServerUtils.EnrichedCredentials & { _id: string };
+ /**
+ * Retrieves the credentials associaed with @param userId
+ * and optionally removes their database id according to @param removeId.
+ */
export const Fetch = async (userId: string, removeId = true): Promise<Opt<StoredCredentials>> => {
- return SanitizedSingletonQuery<StoredCredentials>({ userId }, AuxiliaryCollections.GoogleAuthentication, removeId);
+ return SanitizedSingletonQuery<StoredCredentials>({ userId }, AuxiliaryCollections.GoogleAccess, removeId);
};
+ /**
+ * Writes the @param enrichedCredentials to the database, associated
+ * with @param userId for later retrieval and updating.
+ */
export const Write = async (userId: string, enrichedCredentials: GoogleApiServerUtils.EnrichedCredentials) => {
- return Instance.insert({ userId, canAccess: [], ...enrichedCredentials }, AuxiliaryCollections.GoogleAuthentication);
+ return Instance.insert({ userId, canAccess: [], ...enrichedCredentials }, AuxiliaryCollections.GoogleAccess);
};
+ /**
+ * Updates the @param access_token and @param expiry_date fields
+ * in the stored credentials associated with @param userId.
+ */
export const Update = async (userId: string, access_token: string, expiry_date: number) => {
const entry = await Fetch(userId, false);
if (entry) {
const parameters = { $set: { access_token, expiry_date } };
- return Instance.update(entry._id, parameters, emptyFunction, true, AuxiliaryCollections.GoogleAuthentication);
+ return Instance.update(entry._id, parameters, emptyFunction, true, AuxiliaryCollections.GoogleAccess);
}
};
+ /**
+ * Revokes the credentials associated with @param userId.
+ */
export const Revoke = async (userId: string) => {
const entry = await Fetch(userId, false);
if (entry) {
- Instance.delete({ _id: entry._id }, AuxiliaryCollections.GoogleAuthentication);
+ Instance.delete({ _id: entry._id }, AuxiliaryCollections.GoogleAccess);
}
};
}
- export const LogUpload = async (information: Upload.ImageInformation) => {
- const bundle = {
- _id: Utils.GenerateDeterministicGuid(String(information.contentSize)),
- ...information
- };
- return Instance.insert(bundle, AuxiliaryCollections.GooglePhotosUploadHistory);
- };
-
}
}
diff --git a/src/server/index.ts b/src/server/index.ts
index f26c8a6ab..590affd06 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -5,15 +5,14 @@ import * as path from 'path';
import { Database } from './database';
import { DashUploadUtils } from './DashUploadUtils';
import RouteSubscriber from './RouteSubscriber';
-import initializeServer from './server_Initialization';
+import initializeServer, { resolvedPorts } from './server_Initialization';
import RouteManager, { Method, _success, _permission_denied, _error, _invalid, PublicHandler } from './RouteManager';
import * as qs from 'query-string';
import UtilManager from './ApiManagers/UtilManager';
import { SearchManager } from './ApiManagers/SearchManager';
import UserManager from './ApiManagers/UserManager';
-import { WebSocket } from './Websocket/Websocket';
import DownloadManager from './ApiManagers/DownloadManager';
-import { GoogleCredentialsLoader } from './credentials/CredentialsLoader';
+import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader';
import DeleteManager from "./ApiManagers/DeleteManager";
import PDFManager from "./ApiManagers/PDFManager";
import UploadManager from "./ApiManagers/UploadManager";
@@ -25,8 +24,8 @@ import { yellow } from "colors";
import { DashSessionAgent } from "./DashSession/DashSessionAgent";
import SessionManager from "./ApiManagers/SessionManager";
import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent";
-import { Utils } from "../Utils";
+export const AdminPriviliges: Map<string, boolean> = new Map();
export const onWindows = process.platform === "win32";
export let sessionAgent: AppliedSessionAgent;
export const publicDirectory = path.resolve(__dirname, "public");
@@ -42,6 +41,7 @@ async function preliminaryFunctions() {
await DashUploadUtils.buildFileDirectories();
await Logger.initialize();
await GoogleCredentialsLoader.loadCredentials();
+ SSL.loadCredentials();
GoogleApiServerUtils.processProjectCredentials();
if (process.env.DB !== "MEM") {
await log_execution({
@@ -95,6 +95,11 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
secureHandler: ({ res }) => res.send(true)
});
+ addSupervisedRoute({
+ method: Method.GET,
+ subscription: "/resolvedPorts",
+ secureHandler: ({ res }) => res.send(resolvedPorts)
+ });
const serve: PublicHandler = ({ req, res }) => {
const detector = new mobileDetect(req.headers['user-agent'] || "");
@@ -102,6 +107,42 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
res.sendFile(path.join(__dirname, '../../deploy/' + filename));
};
+ /**
+ * Serves a simple password input box for any
+ */
+ addSupervisedRoute({
+ method: Method.GET,
+ subscription: new RouteSubscriber("admin").add("previous_target"),
+ secureHandler: ({ res, isRelease }) => {
+ const { PASSWORD } = process.env;
+ if (!(isRelease && PASSWORD)) {
+ return res.redirect("/home");
+ }
+ res.render("admin.pug", { title: "Enter Administrator Password" });
+ }
+ });
+
+ addSupervisedRoute({
+ method: Method.POST,
+ subscription: new RouteSubscriber("admin").add("previous_target"),
+ secureHandler: async ({ req, res, isRelease, user: { id } }) => {
+ const { PASSWORD } = process.env;
+ if (!(isRelease && PASSWORD)) {
+ return res.redirect("/home");
+ }
+ const { password } = req.body;
+ const { previous_target } = req.params;
+ let redirect: string;
+ if (password === PASSWORD) {
+ AdminPriviliges.set(id, true);
+ redirect = `/${previous_target.replace(":", "/")}`;
+ } else {
+ redirect = `/admin/${previous_target}`;
+ }
+ res.redirect(redirect);
+ }
+ });
+
addSupervisedRoute({
method: Method.GET,
subscription: ["/home", new RouteSubscriber("doc").add("docId")],
@@ -122,10 +163,6 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
});
logRegistrationOutcome();
-
- // initialize the web socket (bidirectional communication: if a user changes
- // a field on one client, that change must be broadcast to all other clients)
- WebSocket.start(isRelease);
}
@@ -150,9 +187,9 @@ export async function launchServer() {
* log the output of the server process, so it's not ideal for development.
* So, the 'else' clause is exactly what we've always run when executing npm start.
*/
-if (process.env.RELEASE) {
- (sessionAgent = new DashSessionAgent()).launch();
-} else {
- (Database.Instance as Database.Database).doConnect();
- launchServer();
-}
+// if (process.env.RELEASE) {
+// (sessionAgent = new DashSessionAgent()).launch();
+// } else {
+(Database.Instance as Database.Database).doConnect();
+launchServer();
+// }
diff --git a/src/server/remapUrl.ts b/src/server/remapUrl.ts
index 91a3cb6bf..7178add93 100644
--- a/src/server/remapUrl.ts
+++ b/src/server/remapUrl.ts
@@ -1,4 +1,5 @@
import { Database } from "./database";
+import { resolvedPorts } from "./server_Initialization";
//npx ts-node src/server/remapUrl.ts
@@ -34,7 +35,7 @@ async function update() {
if (url.href.includes("localhost") && url.href.includes("Bill")) {
dynfield = true;
- update.$set = { ["fields." + key + ".url"]: `${url.protocol}//dash-web.eastus2.cloudapp.azure.com:1050${url.pathname}` };
+ update.$set = { ["fields." + key + ".url"]: `${url.protocol}//dash-web.eastus2.cloudapp.azure.com:${resolvedPorts.server}${url.pathname}` };
}
}
}
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index add607761..744d4547b 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -7,9 +7,10 @@ import * as cookieParser from 'cookie-parser';
import expressFlash = require('express-flash');
import flash = require('connect-flash');
import { Database } from './database';
-import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/controllers/user_controller';
+import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/AuthenticationManager';
const MongoStore = require('connect-mongo')(session);
import RouteManager from './RouteManager';
+import { WebSocket } from './websocket';
import * as webpack from 'webpack';
const config = require('../../webpack.config');
const compiler = webpack(config);
@@ -19,15 +20,21 @@ import * as fs from 'fs';
import * as request from 'request';
import RouteSubscriber from './RouteSubscriber';
import { publicDirectory } from '.';
-import { logPort, } from './ActionUtilities';
+import { logPort } from './ActionUtilities';
import { blue, yellow } from 'colors';
import * as cors from "cors";
+import { createServer, Server as HttpsServer } from "https";
+import { Server as HttpServer } from "http";
+import { SSL } from './apis/google/CredentialsLoader';
/* RouteSetter is a wrapper around the server that prevents the server
from being exposed. */
export type RouteSetter = (server: RouteManager) => void;
export let disconnect: Function;
+export let resolvedPorts: { server: number, socket: number } = { server: 1050, socket: 4321 };
+export let resolvedServerUrl: string;
+
export default async function InitializeServer(routeSetter: RouteSetter) {
const app = buildWithMiddleware(express());
@@ -45,16 +52,27 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
const isRelease = determineEnvironment();
+ isRelease && !SSL.Loaded && SSL.exit();
+
routeSetter(new RouteManager(app, isRelease));
registerRelativePath(app);
- const serverPort = isRelease ? Number(process.env.serverPort) : 1050;
- const server = app.listen(serverPort, () => {
- logPort("server", serverPort);
- console.log();
- });
- disconnect = async () => new Promise<Error>(resolve => server.close(resolve));
+ let server: HttpServer | HttpsServer;
+ const { serverPort, serverName } = process.env;
+ isRelease && serverPort && (resolvedPorts.server = Number(serverPort));
+ await new Promise<void>(resolve => server = isRelease ?
+ createServer(SSL.Credentials, app).listen(resolvedPorts.server, resolve) :
+ app.listen(resolvedPorts.server, resolve)
+ );
+ logPort("server", resolvedPorts.server);
+
+ resolvedServerUrl = `${isRelease && serverName ? `https://${serverName}.com` : "http://localhost"}:${resolvedPorts.server}`;
+ // initialize the web socket (bidirectional communication: if a user changes
+ // a field on one client, that change must be broadcast to all other clients)
+ await WebSocket.initialize(isRelease, app);
+
+ disconnect = async () => new Promise<Error>(resolve => server.close(resolve));
return isRelease;
}
@@ -94,6 +112,8 @@ function determineEnvironment() {
const label = isRelease ? "release" : "development";
console.log(`\nrunning server in ${color(label)} mode`);
+ // swilkins: I don't think we need to read from ClientUtils.RELEASE anymore. Should be able to invoke process.env.RELEASE
+ // on the client side, thanks to dotenv in webpack.config.js
let clientUtils = fs.readFileSync("./src/client/util/ClientUtils.ts.temp", "utf8");
clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(isRelease))}`;
fs.writeFileSync("./src/client/util/ClientUtils.ts", clientUtils, "utf8");
@@ -120,7 +140,7 @@ function registerAuthenticationRoutes(server: express.Express) {
function registerCorsProxy(server: express.Express) {
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
- server.use("/corsProxy", (req, res) => {
+ server.use("/corsProxy", async (req, res) => {
const requrl = decodeURIComponent(req.url.substring(1));
const referer = req.headers.referer ? decodeURIComponent(req.headers.referer) : "";
@@ -129,8 +149,15 @@ function registerCorsProxy(server: express.Express) {
// then we redirect again to the cors referer and just add the relative path.
if (!requrl.startsWith("http") && req.originalUrl.startsWith("/corsProxy") && referer?.includes("corsProxy")) {
res.redirect(referer + (referer.endsWith("/") ? "" : "/") + requrl);
- }
- else {
+ } else {
+ try {
+ await new Promise<void>((resolve, reject) => {
+ request(requrl).on("response", resolve).on("error", reject);
+ });
+ } catch {
+ console.log(`Malformed CORS url: ${requrl}`);
+ return res.send();
+ }
req.pipe(request(requrl)).on("response", res => {
const headers = Object.keys(res.headers);
headers.forEach(headerName => {
@@ -143,7 +170,7 @@ function registerCorsProxy(server: express.Express) {
}
}
});
- }).pipe(res);
+ }).on("error", () => console.log(`Malformed CORS url: ${requrl}`)).pipe(res);
}
});
}
@@ -152,11 +179,11 @@ function registerRelativePath(server: express.Express) {
server.use("*", (req, res) => {
const relativeUrl = req.originalUrl;
if (!res.headersSent && req.headers.referer?.includes("corsProxy")) { // a request for something by a proxied referrer means it must be a relative reference. So construct a proxied absolute reference here.
- const proxiedRefererUrl = decodeURIComponent(req.headers.referer); // (e.g., http://localhost:1050/corsProxy/https://en.wikipedia.org/wiki/Engelbart)
- const dashServerUrl = proxiedRefererUrl.match(/.*corsProxy\//)![0]; // the dash server url (e.g.: http://localhost:1050/corsProxy/ )
+ const proxiedRefererUrl = decodeURIComponent(req.headers.referer); // (e.g., http://localhost:<port>/corsProxy/https://en.wikipedia.org/wiki/Engelbart)
+ const dashServerUrl = proxiedRefererUrl.match(/.*corsProxy\//)![0]; // the dash server url (e.g.: http://localhost:<port>/corsProxy/ )
const actualReferUrl = proxiedRefererUrl.replace(dashServerUrl, ""); // the url of the referer without the proxy (e.g., : http:s//en.wikipedia.org/wiki/Engelbart)
const absoluteTargetBaseUrl = actualReferUrl.match(/http[s]?:\/\/[^\/]*/)![0]; // the base of the original url (e.g., https://en.wikipedia.org)
- const redirectedProxiedUrl = dashServerUrl + encodeURIComponent(absoluteTargetBaseUrl + relativeUrl); // the new proxied full url (e..g, http://localhost:1050/corsProxy/https://en.wikipedia.org/<somethingelse>)
+ const redirectedProxiedUrl = dashServerUrl + encodeURIComponent(absoluteTargetBaseUrl + relativeUrl); // the new proxied full url (e..g, http://localhost:<port>/corsProxy/https://en.wikipedia.org/<somethingelse>)
res.redirect(redirectedProxiedUrl);
} else if (relativeUrl.startsWith("/search")) { // detect search query and use default search engine
res.redirect(req.headers.referer + "corsProxy/" + encodeURIComponent("http://www.google.com" + relativeUrl));
diff --git a/src/server/Websocket/Websocket.ts b/src/server/websocket.ts
index 37a94cdd3..87af5fa06 100644
--- a/src/server/Websocket/Websocket.ts
+++ b/src/server/websocket.ts
@@ -1,18 +1,22 @@
-import { Utils } from "../../Utils";
-import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent, RoomMessage } from "../Message";
-import { Client } from "../Client";
+import * as fs from 'fs';
+import { logPort } from './ActionUtilities';
+import { Utils } from "../Utils";
+import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent, RoomMessage } from "./Message";
+import { Client } from "./Client";
import { Socket } from "socket.io";
-import { Database } from "../database";
-import { Search } from "../Search";
-import * as io from 'socket.io';
-import YoutubeApi from "../apis/youtube/youtubeApiSample";
-import { GoogleCredentialsLoader } from "../credentials/CredentialsLoader";
-import { logPort } from "../ActionUtilities";
-import { timeMap } from "../ApiManagers/UserManager";
+import { Database } from "./database";
+import { Search } from "./Search";
+import * as sio from 'socket.io';
+import YoutubeApi from "./apis/youtube/youtubeApiSample";
+import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader";
+import { timeMap } from "./ApiManagers/UserManager";
import { green } from "colors";
import { networkInterfaces } from "os";
-import executeImport from "../../scraping/buxton/final/BuxtonImporter";
-import { DocumentsCollection } from "../IDatabase";
+import executeImport from "../scraping/buxton/final/BuxtonImporter";
+import { DocumentsCollection } from "./IDatabase";
+import { createServer, Server } from "https";
+import * as express from "express";
+import { resolvedPorts } from './server_Initialization';
export namespace WebSocket {
@@ -21,19 +25,24 @@ export namespace WebSocket {
export const socketMap = new Map<SocketIO.Socket, string>();
export let disconnect: Function;
+ export async function initialize(isRelease: boolean, app: express.Express) {
+ let io: sio.Server;
+ if (isRelease) {
+ const { socketPort } = process.env;
+ if (socketPort) {
+ resolvedPorts.socket = Number(socketPort);
+ }
+ let socketEndpoint: Server;
+ await new Promise<void>(resolve => socketEndpoint = createServer(SSL.Credentials, app).listen(resolvedPorts.socket, resolve));
+ io = sio(socketEndpoint!, SSL.Credentials as any);
+ } else {
+ io = sio().listen(resolvedPorts.socket);
+ }
+ logPort("websocket", resolvedPorts.socket);
+ console.log();
- export async function start(isRelease: boolean) {
- await preliminaryFunctions();
- initialize(isRelease);
- }
-
- async function preliminaryFunctions() {
- }
- function initialize(isRelease: boolean) {
- const endpoint = io();
- endpoint.on("connection", function (socket: Socket) {
+ io.on("connection", function (socket: Socket) {
_socket = socket;
-
socket.use((_packet, next) => {
const userEmail = socketMap.get(socket);
if (userEmail) {
@@ -129,10 +138,6 @@ export namespace WebSocket {
socket.disconnect(true);
};
});
-
- const socketPort = isRelease ? Number(process.env.socketPort) : 4321;
- endpoint.listen(socketPort);
- logPort("websocket", socketPort);
}
function processGesturePoints(socket: Socket, content: GestureContent) {