aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNaafiyan Ahmed <naafiyan@gmail.com>2022-03-17 16:35:32 -0400
committerNaafiyan Ahmed <naafiyan@gmail.com>2022-03-17 16:35:32 -0400
commitcb5f0847b098d89a1390acf90579b3c7fbc5ac3e (patch)
tree0266608b61495b5903ff419fa26e04fb5fbeef0a /src
parentf1f341b53a1a49427cf7ef40afdcd95a9b0e4e9d (diff)
parent5c874c6829d9957696dfe61014173b6800c864df (diff)
Merge branch 'naafi-linking' of https://github.com/brown-dash/Dash-Web into naafi-linking
Diffstat (limited to 'src')
-rw-r--r--src/.DS_Storebin8196 -> 8196 bytes
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/ClientRecommender.tsx421
-rw-r--r--src/client/apis/GoogleAuthenticationManager.tsx4
-rw-r--r--src/client/cognitive_services/CognitiveServices.ts3
-rw-r--r--src/client/documents/Documents.ts90
-rw-r--r--src/client/util/CurrentUserUtils.ts195
-rw-r--r--src/client/util/DictationManager.ts353
-rw-r--r--src/client/util/DocumentManager.ts8
-rw-r--r--src/client/util/DragManager.ts2
-rw-r--r--src/client/util/DropConverter.ts4
-rw-r--r--src/client/util/History.ts6
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx7
-rw-r--r--src/client/util/LinkManager.ts2
-rw-r--r--src/client/util/ScriptManager.ts12
-rw-r--r--src/client/util/Scripting.ts109
-rw-r--r--src/client/util/ScriptingGlobals.ts81
-rw-r--r--src/client/util/UndoManager.ts3
-rw-r--r--src/client/views/DocComponent.tsx12
-rw-r--r--src/client/views/DocumentDecorations.tsx24
-rw-r--r--src/client/views/GestureOverlay.tsx12
-rw-r--r--src/client/views/InkControlPtHandles.tsx1
-rw-r--r--src/client/views/InkingStroke.tsx7
-rw-r--r--src/client/views/Main.tsx16
-rw-r--r--src/client/views/MainView.tsx50
-rw-r--r--src/client/views/OverlayView.tsx8
-rw-r--r--src/client/views/PropertiesButtons.tsx19
-rw-r--r--src/client/views/PropertiesView.tsx11
-rw-r--r--src/client/views/ScriptBox.tsx20
-rw-r--r--src/client/views/ScriptingRepl.tsx7
-rw-r--r--src/client/views/StyleProvider.tsx2
-rw-r--r--src/client/views/TemplateMenu.tsx6
-rw-r--r--src/client/views/animationtimeline/Keyframe.tsx21
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx9
-rw-r--r--src/client/views/animationtimeline/TimelineMenu.tsx5
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx12
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx11
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx20
-rw-r--r--src/client/views/collections/CollectionMenu.tsx4
-rw-r--r--src/client/views/collections/CollectionPileView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx64
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx17
-rw-r--r--src/client/views/collections/CollectionStaffView.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx11
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx15
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx6
-rw-r--r--src/client/views/collections/CollectionView.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx249
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx23
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx7
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx9
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx11
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx12
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx14
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx27
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTable.tsx1
-rw-r--r--src/client/views/nodes/AudioBox.tsx150
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx17
-rw-r--r--src/client/views/nodes/ColorBox.tsx7
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx9
-rw-r--r--src/client/views/nodes/DocumentIcon.tsx2
-rw-r--r--src/client/views/nodes/DocumentLinksButton.scss4
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx24
-rw-r--r--src/client/views/nodes/DocumentView.scss5
-rw-r--r--src/client/views/nodes/DocumentView.tsx72
-rw-r--r--src/client/views/nodes/EquationBox.tsx11
-rw-r--r--src/client/views/nodes/FilterBox.tsx17
-rw-r--r--src/client/views/nodes/FunctionPlotBox.tsx2
-rw-r--r--src/client/views/nodes/ImageBox.tsx19
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/LabelBox.tsx9
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx6
-rw-r--r--src/client/views/nodes/LinkBox.tsx7
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx9
-rw-r--r--src/client/views/nodes/PDFBox.scss1
-rw-r--r--src/client/views/nodes/PDFBox.tsx64
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx14
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx34
-rw-r--r--src/client/views/nodes/SliderBox.tsx15
-rw-r--r--src/client/views/nodes/VideoBox.tsx68
-rw-r--r--src/client/views/nodes/WebBox.scss7
-rw-r--r--src/client/views/nodes/WebBox.tsx176
-rw-r--r--src/client/views/nodes/WebBoxRenderer.js395
-rw-r--r--src/client/views/nodes/button/ButtonScripts.ts6
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx199
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx25
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts24
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts26
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx12
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx29
-rw-r--r--src/client/views/pdf/PDFViewer.tsx9
-rw-r--r--src/client/views/search/SearchBox.tsx18
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.tsx5
-rw-r--r--src/fields/CursorField.ts2
-rw-r--r--src/fields/DateField.ts4
-rw-r--r--src/fields/Doc.ts51
-rw-r--r--src/fields/InkField.ts6
-rw-r--r--src/fields/List.ts6
-rw-r--r--src/fields/ObjectField.ts6
-rw-r--r--src/fields/Proxy.ts2
-rw-r--r--src/fields/RichTextField.ts6
-rw-r--r--src/fields/SchemaHeaderField.ts14
-rw-r--r--src/fields/ScriptField.ts91
-rw-r--r--src/fields/Types.ts10
-rw-r--r--src/fields/URLField.ts2
-rw-r--r--src/mobile/MobileInterface.tsx20
-rw-r--r--src/scraping/buxton/final/BuxtonImporter.ts2
-rw-r--r--src/server/ActionUtilities.ts2
-rw-r--r--src/server/ApiManagers/PDFManager.ts5
-rw-r--r--src/server/ApiManagers/UploadManager.ts19
-rw-r--r--src/server/ApiManagers/UserManager.ts2
-rw-r--r--src/server/DashSession/DashSessionAgent.ts2
-rw-r--r--src/server/DashSession/Session/agents/monitor.ts2
-rw-r--r--src/server/DashSession/Session/agents/promisified_ipc_manager.ts2
-rw-r--r--src/server/DashSession/Session/agents/server_worker.ts2
-rw-r--r--src/server/DashUploadUtils.ts14
-rw-r--r--src/server/RouteManager.ts2
-rw-r--r--src/server/Search.ts2
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts4
-rw-r--r--src/server/authentication/AuthenticationManager.ts15
-rw-r--r--src/server/authentication/Passport.ts6
-rw-r--r--src/server/remapUrl.ts2
-rw-r--r--src/server/server_Initialization.ts39
123 files changed, 1925 insertions, 1980 deletions
diff --git a/src/.DS_Store b/src/.DS_Store
index bdc161da2..b0987293b 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/Utils.ts b/src/Utils.ts
index f2d9e7766..d0d891f77 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -2,8 +2,8 @@ import v4 = require('uuid/v4');
import v5 = require("uuid/v5");
import { ColorState } from 'react-color';
import { Socket } from 'socket.io';
-import { Message } from './server/Message';
import { Colors } from './client/views/global/globalEnums';
+import { Message } from './server/Message';
import Color = require('color');
export namespace Utils {
diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
deleted file mode 100644
index 1d4653471..000000000
--- a/src/client/ClientRecommender.tsx
+++ /dev/null
@@ -1,421 +0,0 @@
-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";
-import { observable, action, computed, reaction } from "mobx";
-// var assert = require('assert');
-// var sw = require('stopword');
-// var FeedParser = require('feedparser');
-// var https = require('https');
-import "./ClientRecommender.scss";
-import { JSXElement } from "babel-types";
-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";
-
-export interface RecommenderProps {
- title: string;
-}
-
-/**
- * actualDoc: datadoc
- * vectorDoc: mean vector of text
- * score: similarity score to main doc
- */
-
-export interface RecommenderDocument {
- actualDoc: Doc;
- vectorDoc: number[];
- score: number;
-}
-
-const fieldkey = "data";
-
-@observer
-export class ClientRecommender extends React.Component<RecommenderProps> {
-
- static Instance: ClientRecommender;
- private mainDoc?: RecommenderDocument;
- private docVectors: Set<RecommenderDocument> = new Set();
- public _queries: string[] = [];
-
- @observable private corr_matrix = [[0, 0], [0, 0]]; // for testing
-
- constructor(props: RecommenderProps) {
- super(props);
- if (!ClientRecommender.Instance) ClientRecommender.Instance = this;
- ClientRecommender.Instance.docVectors = new Set();
- //ClientRecommender.Instance.corr_matrix = [[0, 0], [0, 0]];
- }
-
- @action
- public reset_docs() {
- ClientRecommender.Instance.docVectors = new Set();
- ClientRecommender.Instance.mainDoc = undefined;
- ClientRecommender.Instance.corr_matrix = [[0, 0], [0, 0]];
- }
-
- /***
- * Computes the cosine similarity between two vectors in Euclidean space.
- */
-
- private distance(vector1: number[], vector2: number[], metric: string = "cosine") {
- // assert(vector1.length === vector2.length, "Vectors are not the same length");
- let similarity: number;
- switch (metric) {
- case "cosine":
- var dotproduct = 0;
- var mA = 0;
- var mB = 0;
- for (let i = 0; i < vector1.length; i++) { // here you missed the i++
- dotproduct += (vector1[i] * vector2[i]);
- mA += (vector1[i] * vector1[i]);
- mB += (vector2[i] * vector2[i]);
- }
- mA = Math.sqrt(mA);
- mB = Math.sqrt(mB);
- similarity = (dotproduct) / ((mA) * (mB)); // here you needed extra brackets
- return similarity;
- case "euclidian":
- var sum = 0;
- for (let i = 0; i < vector1.length; i++) {
- sum += Math.pow(vector1[i] - vector2[i], 2);
- }
- similarity = Math.sqrt(sum);
- return similarity;
- default:
- return 0;
- }
- }
-
- /**
- * Returns list of {doc, similarity (to main doc)} in increasing score
- */
-
- public computeSimilarities(distance_metric: string) {
- const parameters: any = {};
- Networking.PostToServer("/IBMAnalysis", parameters).then(response => {
- console.log("ANALYSIS RESULTS! ", response);
- });
- ClientRecommender.Instance.docVectors.forEach((doc: RecommenderDocument) => {
- if (ClientRecommender.Instance.mainDoc) {
- const distance = ClientRecommender.Instance.distance(ClientRecommender.Instance.mainDoc.vectorDoc, doc.vectorDoc, distance_metric);
- doc.score = distance;
- }
- }
- );
- const doclist = Array.from(ClientRecommender.Instance.docVectors);
- if (distance_metric === "euclidian") {
- doclist.sort((a: RecommenderDocument, b: RecommenderDocument) => a.score - b.score);
- }
- else {
- doclist.sort((a: RecommenderDocument, b: RecommenderDocument) => b.score - a.score);
- }
- return doclist;
- }
-
- /***
- * Computes the mean of a set of vectors
- */
-
- public mean(paragraph: Set<number[]>) {
- const n = 512;
- const num_words = paragraph.size;
- let meanVector = new Array<number>(n).fill(0); // mean vector
- if (num_words > 0) { // check to see if paragraph actually was vectorized
- paragraph.forEach((wordvec: number[]) => {
- for (let i = 0; i < n; i++) {
- meanVector[i] += wordvec[i];
- }
- });
- meanVector = meanVector.map(x => x / num_words);
- }
- return meanVector;
- }
-
- /***
- * Processes sentence vector as Recommender Document, adds to Doc Set.
- */
-
- public processVector(vector: number[], dataDoc: Doc, isMainDoc: boolean) {
- if (vector.length > 0) {
- const internalDoc: RecommenderDocument = { actualDoc: dataDoc, vectorDoc: vector, score: 0 };
- ClientRecommender.Instance.addToDocSet(internalDoc, isMainDoc);
- }
- }
-
- /***
- * Adds to Doc set. Updates mainDoc (one clicked) if necessary.
- */
-
- private addToDocSet(internalDoc: RecommenderDocument, isMainDoc: boolean) {
- if (ClientRecommender.Instance.docVectors) {
- if (isMainDoc) ClientRecommender.Instance.mainDoc = internalDoc;
- ClientRecommender.Instance.docVectors.add(internalDoc);
- }
- }
-
- /***
- * Generates tags for an image using Cognitive Services
- */
-
- generateMetadata = async (dataDoc: Doc, extDoc: Doc, threshold: Confidence = Confidence.Excellent) => {
- const converter = (results: any) => {
- const tagDoc = new Doc;
- const tagsList = new List();
- results.tags.map((tag: Tag) => {
- tagsList.push(tag.name);
- const sanitized = tag.name.replace(" ", "_");
- tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
- });
- extDoc.generatedTags = tagsList;
- tagDoc.title = "Generated Tags Doc";
- tagDoc.confidence = threshold;
- return tagDoc;
- };
- const url = this.url(dataDoc);
- if (url) {
- return CognitiveServices.Image.Appliers.ProcessImage(extDoc, ["generatedTagsDoc"], url, Service.ComputerVision, converter);
- }
- }
-
- /***
- * Gets URL of image
- */
-
- private url(dataDoc: Doc) {
- const data = Cast(Doc.GetProto(dataDoc)[fieldkey], ImageField);
- return data ? data.url.href : undefined;
- }
-
- /***
- * Uses Cognitive Services to extract keywords from a document
- */
-
- public async extractText(dataDoc: Doc, extDoc: Doc, internal: boolean = true, api: string = "bing", isMainDoc: boolean = false, image: boolean = false) {
- // STEP 1. Consolidate data of document. Depends on type of document.
- let data: string = "";
- let taglist: FieldResult<List<string>> = undefined;
- if (image) {
- if (!extDoc.generatedTags) await this.generateMetadata(dataDoc, extDoc); // TODO: Automatically generate tags. Need to ask Sam about this.
- if (extDoc.generatedTags) {
- taglist = Cast(extDoc.generatedTags, listSpec("string"));
- taglist!.forEach(tag => {
- data += tag + ", ";
- });
- }
- }
- else {
- const fielddata = Cast(dataDoc.data, RichTextField, null);
- data = fielddata?.Text || "";
- }
-
- // STEP 2. Upon receiving response from Text Cognitive Services, do additional processing on keywords.
- // Currently we are still using Cognitive Services for internal recommendations, but in the future this might not be necessary.
-
- const converter = async (results: any, data: string, isImage: boolean = false) => {
- let keyterms = new List<string>(); // raw keywords
- let kp_string: string = ""; // keywords*frequency concatenated into a string. input into TF
- let highKP: string[] = [""]; // most frequent keyphrase
- let high = 0;
-
- if (isImage) { // no keyphrase processing necessary
- kp_string = data;
- if (taglist) {
- keyterms = taglist;
- highKP = [taglist[0]];
- }
- }
- else { // text processing
- results.documents.forEach((doc: any) => {
- const keyPhrases = doc.keyPhrases; // returned by Cognitive Services
- keyPhrases.map((kp: string) => {
- keyterms.push(kp);
- const frequency = this.countFrequencies(kp, data); // frequency of keyphrase in paragraph
- kp_string += kp + ", "; // ensures that if frequency is 0 for some reason kp is still added
- for (let i = 0; i < frequency - 1; i++) {
- kp_string += kp + ", "; // weights repeated keywords higher
- }
- // replaces highKP with new one
- if (frequency > high) {
- high = frequency;
- highKP = [kp];
- }
- // appends to current highKP phrase
- else if (frequency === high) {
- highKP.push(kp);
- }
- });
- });
- }
- if (kp_string.length > 2) kp_string = kp_string.substring(0, kp_string.length - 2); // strips extra comma and space if there are a lot of keywords
- console.log("kp_string: ", kp_string);
-
- let ext_recs = "";
- // Pushing keyword extraction to IBM for external recommendations. Should shift to internal eventually.
- if (!internal) {
- const parameters: any = {
- 'language': 'en',
- 'text': data,
- 'features': {
- 'keywords': {
- 'sentiment': true,
- 'emotion': true,
- 'limit': 3
- }
- }
- };
- await Networking.PostToServer("/IBMAnalysis", parameters).then(response => {
- const sorted_keywords = response.result.keywords;
- if (sorted_keywords.length > 0) {
- console.log("IBM keyphrase", sorted_keywords[0]);
- highKP = [];
- for (let i = 0; i < 5; i++) {
- if (sorted_keywords[i]) {
- highKP.push(sorted_keywords[i].text);
- }
- }
- keyterms = new List<string>(highKP);
- }
- });
- //let kpqv = new KeyphraseQueryView({ keyphrases: ["hello"] });
- ext_recs = await this.sendRequest([highKP[0]], api);
- }
-
- // keyterms: list for extDoc, kp_string: input to TF, ext_recs: {titles, urls} of retrieved results from highKP query
- return { keyterms: keyterms, external_recommendations: ext_recs, kp_string: [kp_string] };
- };
-
- // STEP 3: Start recommendation pipeline. Branches off into internal and external in Cognitive Services
- if (data !== "") {
- return CognitiveServices.Text.Appliers.analyzer(dataDoc, extDoc, ["key words"], data, converter, isMainDoc, internal);
- }
- return;
- }
-
- /**
- *
- * Counts frequencies of keyphrase in paragraph.
- */
-
- private countFrequencies(keyphrase: string, paragraph: string) {
- const data = paragraph.split(/ |\n/); // splits by new lines and spaces
- const kp_array = keyphrase.split(" ");
- const num_keywords = kp_array.length;
- const par_length = data.length;
- let frequency = 0;
- // slides keyphrase windows across paragraph and checks if it matches with corresponding paragraph slice
- for (let i = 0; i <= par_length - num_keywords; i++) {
- const window = data.slice(i, i + num_keywords);
- if (JSON.stringify(window).toLowerCase() === JSON.stringify(kp_array).toLowerCase() || kp_array.every(val => window.includes(val))) {
- frequency++;
- }
- }
- return frequency;
- }
-
- /**
- *
- * API for sending arXiv request.
- */
-
- private async sendRequest(keywords: string[], api: string) {
- let query = "";
- keywords.forEach((kp: string) => query += " " + kp);
- if (api === "arxiv") {
- return new Promise<any>(resolve => {
- this.arxivrequest(query).then(resolve);
- });
- }
- else if (api === "bing") {
- return new Promise<any>(resolve => {
- this.bingWebSearch(query).then(resolve);
- });
- }
- else {
- console.log("no api specified :(");
- }
-
- }
-
- /**
- * Request to Bing API. Most of code is in Cognitive Services.
- */
-
- bingWebSearch = async (query: string) => {
- const converter = async (results: any) => {
- const title_vals: string[] = [];
- const url_vals: string[] = [];
- results.webPages.value.forEach((doc: any) => {
- title_vals.push(doc.name);
- url_vals.push(doc.url);
- });
- return { title_vals, url_vals };
- };
- return CognitiveServices.BingSearch.Appliers.analyzer(query, converter);
- }
-
- /**
- * Actual request to the arXiv server for ML articles.
- */
-
- arxivrequest = async (query: string) => {
- const xhttp = new XMLHttpRequest();
- const serveraddress = "http://export.arxiv.org/api";
- const maxresults = 5;
- const endpoint = serveraddress + "/query?search_query=all:" + query + "&start=0&max_results=" + maxresults.toString();
- const promisified = (resolve: any, reject: any) => {
- xhttp.onreadystatechange = function () {
- if (this.readyState === 4) {
- const result = xhttp.response;
- const xml = xhttp.responseXML;
- console.log("arXiv Result: ", xml);
- switch (this.status) {
- case 200:
- const title_vals: string[] = [];
- const url_vals: string[] = [];
- if (xml) {
- const titles = xml.getElementsByTagName("title");
- let counter = 1;
- if (titles && titles.length > 1) {
- while (counter <= maxresults) {
- const title = titles[counter].childNodes[0].nodeValue!;
- title_vals.push(title);
- counter++;
- }
- }
- const ids = xml.getElementsByTagName("id");
- counter = 1;
- if (ids && ids.length > 1) {
- while (counter <= maxresults) {
- const url = ids[counter].childNodes[0].nodeValue!;
- url_vals.push(url);
- counter++;
- }
- }
- }
- return resolve({ title_vals, url_vals });
- case 400:
- default:
- return reject(result);
- }
- }
- };
- xhttp.open("GET", endpoint, true);
- xhttp.send();
- };
- return new Promise<any>(promisified);
- }
-
- render() {
- return (<div className="wrapper">
- </div>);
- }
-
-} \ No newline at end of file
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index cda108058..855f48f7e 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import { Opt } from "../../fields/Doc";
import { Networking } from "../Network";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
import { MainViewModal } from "../views/MainViewModal";
import "./GoogleAuthenticationManager.scss";
@@ -165,4 +165,4 @@ export class GoogleAuthenticationManager extends React.Component<{}> {
}
-Scripting.addGlobal("GoogleAuthenticationManager", GoogleAuthenticationManager); \ No newline at end of file
+ScriptingGlobals.add("GoogleAuthenticationManager", GoogleAuthenticationManager); \ No newline at end of file
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 80961af14..2b2931a97 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -6,7 +6,6 @@ import { InkData } from "../../fields/InkField";
import { UndoManager } from "../util/UndoManager";
import requestPromise = require("request-promise");
import { List } from "../../fields/List";
-import { ClientRecommender } from "../ClientRecommender";
type APIManager<D> = { converter: BodyConverter<D>, requester: RequestExecutor };
type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise<string>;
@@ -379,7 +378,7 @@ export namespace CognitiveServices {
indices.forEach((ind: any) => {
vectorValues.push(wordvecs[ind]);
});
- ClientRecommender.Instance.processVector(vectorValues, dataDoc, mainDoc);
+ //ClientRecommender.Instance.processVector(vectorValues, dataDoc, mainDoc);
} // adds document to internal doc set
else {
console.log("unsuccessful :( word(s) not in vocabulary");
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index df573a377..e2f82fc62 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -23,7 +23,7 @@ import { DocumentManager } from "../util/DocumentManager";
import { dropActionType } from "../util/DragManager";
import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
import { LinkManager } from "../util/LinkManager";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
import { undoBatch, UndoManager } from "../util/UndoManager";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
@@ -61,7 +61,6 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
import { DocumentType } from "./DocumentTypes";
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { MapBox } from "../views/nodes/MapBox/MapBox";
-const path = require('path');
const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
class EmptyBox {
@@ -235,7 +234,7 @@ export class DocumentOptions {
docColorBtn?: string;
userColorBtn?: string;
canClick?: string;
- script?: string;
+ script?: ScriptField;
numBtnType?: string;
numBtnMax?: number;
numBtnMin?: number;
@@ -361,31 +360,34 @@ export namespace Docs {
const TemplateMap: TemplateMap = new Map([
[DocumentType.RTF, {
layout: { view: FormattedTextBox, dataField: "text" },
- options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
+ options: {
+ _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true,
+ links: "@links(self)"
+ }
}],
[DocumentType.SEARCH, {
layout: { view: SearchBox, dataField: defaultDataKey },
- options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _width: 400, links: "@links(self)" }
}],
[DocumentType.FILTER, {
layout: { view: FilterBox, dataField: defaultDataKey },
- options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _width: 400, links: "@links(self)" }
}],
[DocumentType.COLOR, {
layout: { view: ColorBox, dataField: defaultDataKey },
- options: { _nativeWidth: 220, _nativeHeight: 300, links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _nativeWidth: 220, _nativeHeight: 300, links: "@links(self)" }
}],
[DocumentType.IMG, {
layout: { view: ImageBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.WEB, {
layout: { view: WebBox, dataField: defaultDataKey },
- options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
}],
[DocumentType.COL, {
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: "@links(self)" }
}],
[DocumentType.KVP, {
layout: { view: KeyValueBox, dataField: defaultDataKey },
@@ -393,19 +395,19 @@ export namespace Docs {
}],
[DocumentType.VID, {
layout: { view: VideoBox, dataField: defaultDataKey },
- options: { _currentTimecode: 0, links: ComputedField.MakeFunction("links(self)") as any },
+ options: { _currentTimecode: 0, links: "@links(self)" },
}],
[DocumentType.AUDIO, {
layout: { view: AudioBox, dataField: defaultDataKey },
- options: { _height: 100, backgroundColor: "lightGray", links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" }
}],
[DocumentType.PDF, {
layout: { view: PDFBox, dataField: defaultDataKey },
- options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
}],
[DocumentType.MAP, {
layout: { view: MapBox, dataField: defaultDataKey },
- options: { _height: 600, _width: 800, links: ComputedField.MakeFunction("links(self)") as any }
+ options: { _height: 600, _width: 800, links: "@links(self)" }
}],
[DocumentType.IMPORT, {
layout: { view: DirectoryImportBox, dataField: defaultDataKey },
@@ -416,7 +418,7 @@ export namespace Docs {
options: {
childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description",
backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area
- links: ComputedField.MakeFunction("links(self)") as any,
+ links: "@links(self)",
_removeDropProperties: new List(["_layerTags", "isLinkButton"]),
}
}],
@@ -432,61 +434,61 @@ export namespace Docs {
}],
[DocumentType.SCRIPTING, {
layout: { view: ScriptingBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.YOUTUBE, {
layout: { view: YoutubeBox, dataField: defaultDataKey }
}],
[DocumentType.LABEL, {
layout: { view: LabelBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.EQUATION, {
layout: { view: EquationBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any, hideResizeHandles: true, hideDecorationTitle: true }
+ options: { links: "@links(self)", hideResizeHandles: true, hideDecorationTitle: true }
}],
[DocumentType.FUNCPLOT, {
layout: { view: FunctionPlotBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.BUTTON, {
layout: { view: LabelBox, dataField: "onClick" },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.SLIDER, {
layout: { view: SliderBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.PRES, {
layout: { view: PresBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.FONTICON, {
layout: { view: FontIconBox, dataField: defaultDataKey },
- options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: ComputedField.MakeFunction("links(self)") as any },
+ options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" },
}],
[DocumentType.WEBCAM, {
layout: { view: DashWebRTCVideo, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.PRESELEMENT, {
layout: { view: PresElementBox, dataField: defaultDataKey }
}],
[DocumentType.MARKER, {
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true }
+ options: { links: "@links(self)", hideLinkButton: true }
}],
[DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
layout: { view: InkingStroke, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.SCREENSHOT, {
layout: { view: ScreenshotBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
[DocumentType.COMPARISON, {
layout: { view: ComparisonBox, dataField: defaultDataKey },
- options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: ComputedField.MakeFunction("links(self)") as any }
+ options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" }
}],
[DocumentType.GROUPDB, {
data: new List<Doc>(),
@@ -495,7 +497,7 @@ export namespace Docs {
}],
[DocumentType.GROUP, {
layout: { view: EmptyBox, dataField: defaultDataKey },
- options: { links: ComputedField.MakeFunction("links(self)") as any }
+ options: { links: "@links(self)" }
}],
]);
@@ -592,6 +594,11 @@ export namespace Docs {
system: true, _layoutKey: "layout", title, type, baseProto: true, x: 0, y: 0, _width: 300, ...(template.options || {}),
layout: layout.view?.LayoutString(layout.dataField), data: template.data, layout_keyValue: KeyValueBox.LayoutString("")
};
+ Object.entries(options).map(pair => {
+ if (typeof pair[1] === "string" && pair[1].startsWith("@")) {
+ (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
+ }
+ });
return Doc.assign(new Doc(prototypeId, true), options as any, undefined, true);
}
}
@@ -655,7 +662,7 @@ export namespace Docs {
export function ImageDocument(url: string, options: DocumentOptions = {}) {
const imgField = new ImageField(url);
- return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
+ return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
}
export function PresDocument(initial: List<Doc> = new List(), options: DocumentOptions = {}) {
@@ -758,7 +765,7 @@ export namespace Docs {
I.data = new InkField(points);
I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
I["acl-Override"] = "None";
- I.links = ComputedField.MakeFunction("links(self)") as any;
+ I.links = "@links(self)";
I[Initializing] = false;
return I;
}
@@ -1443,13 +1450,13 @@ export namespace DocUtils {
if (doc) {
const proto = Doc.GetProto(doc);
proto.text = result.rawText;
- proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+ proto.fileUpload = pathname.replace(/.*\//, "").replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
if (Upload.isImageInformation(result)) {
const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
- proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation;
+ proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation ?? ((StrCast((result.exifData?.data as any)?.Orientation).includes("Rotate 90")) ? 5 : undefined);
proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
- if (Number(result.exifData?.data?.image?.Orientation) >= 5) {
+ if (NumCast(proto["data-nativeOrientation"]) >= 5) {
proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
}
@@ -1472,27 +1479,28 @@ export namespace DocUtils {
export async function uploadYoutubeVideo(videoId: string, options: DocumentOptions) {
const generatedDocuments: Doc[] = [];
for (const { source: { name, type }, result } of await Networking.UploadYoutubeToServer(videoId)) {
- processFileupload(generatedDocuments, name, type, result, options);
+ name && type && processFileupload(generatedDocuments, name, type, result, options);
}
return generatedDocuments;
}
export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
const generatedDocuments: Doc[] = [];
- for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
- processFileupload(generatedDocuments, name, type, result, options);
+ const upfiles = await Networking.UploadFilesToServer(files);
+ for (const { source: { name, type }, result } of upfiles) {
+ name && type && processFileupload(generatedDocuments, name, type, result, options);
}
return generatedDocuments;
}
}
-Scripting.addGlobal("Docs", Docs);
-Scripting.addGlobal(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
-Scripting.addGlobal(function generateLinkTitle(self: Doc) {
+ScriptingGlobals.add("Docs", Docs);
+ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
+ScriptingGlobals.add(function generateLinkTitle(self: Doc) {
const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : "<?>";
const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : "<?>";
const relation = self.linkRelationship || "to";
return `${anchor1title} (${relation}) ${anchor2title}`;
});
-Scripting.addGlobal(function openTabAlias(tab: Doc) {
+ScriptingGlobals.add(function openTabAlias(tab: Doc) {
CollectionDockingView.AddSplit(Doc.MakeAlias(tab), "right");
}); \ No newline at end of file
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index a8b0da369..c7f293f2c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,5 +1,4 @@
import { computed, observable, reaction } from "mobx";
-import * as rp from 'request-promise';
import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { InkTool } from "../../fields/InkField";
@@ -16,9 +15,7 @@ import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { Docs, DocumentOptions, DocUtils } from "../documents/Documents";
import { DocumentType } from "../documents/DocumentTypes";
-import { Networking } from "../Network";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
import { TreeView } from "../views/collections/TreeView";
import { Colors } from "../views/global/globalEnums";
@@ -32,13 +29,14 @@ import { DragManager } from "./DragManager";
import { makeTemplate } from "./DropConverter";
import { HistoryUtil } from "./History";
import { LinkManager } from "./LinkManager";
-import { Scripting } from "./Scripting";
+import { ScriptingGlobals } from "./ScriptingGlobals";
import { SearchUtil } from "./SearchUtil";
import { SelectionManager } from "./SelectionManager";
import { ColorScheme } from "./SettingsManager";
import { SharingManager } from "./SharingManager";
import { SnappingManager } from "./SnappingManager";
import { UndoManager } from "./UndoManager";
+import * as rp from 'request-promise';
interface Button {
title?: string;
@@ -51,7 +49,6 @@ interface Button {
numBtnMax?: number;
switchToggle?: boolean;
script?: string;
- checkResult?: string;
width?: number;
list?: string[];
ignoreClick?: boolean;
@@ -162,37 +159,37 @@ export class CurrentUserUtils {
});
}
- if (doc["template-button-switch"] === undefined) {
- const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
-
- const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true });
- const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true });
- const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true });
- const labelTemplate = {
- doc: {
- type: "doc", content: [{
- type: "paragraph",
- content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }]
- }]
- },
- selection: { type: "text", anchor: 1, head: 1 },
- storedMarks: []
- };
- Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS");
- Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'");
- // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'");
- // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true");
- Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]");
- // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false");
- const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true });
- box.isTemplateDoc = makeTemplate(box, true, "switch");
-
- doc["template-button-switch"] = CurrentUserUtils.createToolButton({
- onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
- dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true,
- btnType: ButtonType.ToolButton
- });
- }
+ // if (doc["template-button-switch"] === undefined) {
+ // const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
+
+ // const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true });
+ // const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true });
+ // const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true });
+ // const labelTemplate = {
+ // doc: {
+ // type: "doc", content: [{
+ // type: "paragraph",
+ // content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }]
+ // }]
+ // },
+ // selection: { type: "text", anchor: 1, head: 1 },
+ // storedMarks: []
+ // };
+ // Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS");
+ // Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'");
+ // // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'");
+ // // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true");
+ // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]");
+ // // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false");
+ // const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true });
+ // box.isTemplateDoc = makeTemplate(box, true, "switch");
+
+ // doc["template-button-switch"] = CurrentUserUtils.createToolButton({
+ // onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
+ // dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true,
+ // btnType: ButtonType.ToolButton
+ // });
+ // }
if (doc["template-button-detail"] === undefined) {
const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create;
@@ -538,11 +535,11 @@ export class CurrentUserUtils {
// setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
static async setupCreatorButtons(doc: Doc) {
let alreadyCreatedButtons: string[] = [];
- const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null);
+ const dragCreatorSet = Cast(doc.myItemCreators, Doc, null);
if (dragCreatorSet) {
- const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc));
+ const dragCreators = Cast(dragCreatorSet.data, listSpec(Doc));
if (dragCreators) {
- const dragDocs = await Promise.all(dragCreators);
+ const dragDocs = await Promise.all(Array.from(dragCreators));
alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
}
}
@@ -735,12 +732,16 @@ export class CurrentUserUtils {
];
return docProtoData.map(data => Docs.Create.FontIconDocument({
_nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon,
- _dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick,
+ _dropAction: data.pointerDown ? "copy" : undefined,
+ ignoreClick: data.ignoreClick,
onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
clipboard: data.clipboard,
- onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
+ onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
+ onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
backgroundColor: data.backgroundColor,
- _removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory, system: true
+ _removeDropProperties: new List<string>(["dropAction"]),
+ dragFactory: data.dragFactory,
+ system: true
}));
}
@@ -987,37 +988,37 @@ export class CurrentUserUtils {
title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia",
"Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
- script: 'setFont'
+ script: 'setFont(value, _readOnly_)'
},
- { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize' },
- { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor' },
- { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', checkResult: 'toggleBold(true)' },
- { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', checkResult: 'toggleItalic(true)' },
- { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', checkResult: 'toggleUnderline(true)' },
- { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet")', checkResult: 'setBulletList("bullet", true)' },
- { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal")', checkResult: 'setBulletList("decimal", true)' },
+ { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
+ { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
+ { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' },
+ { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' },
+ { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
+ { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
+ { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
// { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
// { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
// { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
- { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left")', checkResult: 'setAlignment("left", true)' },
- { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center")', checkResult: 'setAlignment("center", true)' },
- { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right")', checkResult: 'setAlignment("right", true)' },
+ { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
+ { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
+ { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
];
return tools;
}
static inkTools(doc: Doc) {
const tools: Button[] = [
- { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', checkResult: 'setActiveInkTool("pen" , true)' },
- { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser")', checkResult: 'setActiveInkTool("eraser" , true)' },
- // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', checkResult: 'setActiveInkTool("highlighter", true)' },
- { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', checkResult: 'setActiveInkTool("circle" , true)' },
- // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', checkResult: 'setActiveInkTool("square" , true)' },
- { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', checkResult: 'setActiveInkTool("line" , true)' },
- { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor" },
- { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth' },
- { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor' },
+ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen", _readOnly_)' },
+ { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' },
+ // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' },
+ { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
+ // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
+ { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' },
+ { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
+ { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
+ { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
];
return tools;
}
@@ -1031,8 +1032,7 @@ export class CurrentUserUtils {
btnType: ButtonType.ToggleButton,
buttonText: "Show Preview",
icon: "eye",
- click: 'toggleSchemaPreview()',
- checkResult: 'toggleSchemaPreview(true)'
+ click: 'toggleSchemaPreview(_readOnly_)',
},
];
return tools;
@@ -1041,10 +1041,10 @@ export class CurrentUserUtils {
static webTools(doc: Doc) {
const tools: Button[] =
[
- { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack()' },
- { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward()' },
+ { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' },
+ { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' },
//{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' },
- { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL' },
+ { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' },
];
return tools;
@@ -1059,17 +1059,17 @@ export class CurrentUserUtils {
CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
CollectionViewType.Grid],
- script: 'setView',
+ script: 'setView(value, _readOnly_)',
}, // Always show
{
title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
- script: "setBackgroundColor", hidden: 'selectedDocumentType()'
+ script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()'
}, // Only when a document is selected
{
title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
- script: "setHeaderColor", hidden: 'selectedDocumentType()',
+ script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
}, // Only when a document is selected
- { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+ { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
// { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected
{ title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
{ title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available
@@ -1083,7 +1083,7 @@ export class CurrentUserUtils {
if (doc.contextMenuBtns === undefined) {
const docList: Doc[] = [];
- (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded, checkResult }) => {
+ (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
const menuDocList: Doc[] = [];
if (subMenu) {
// default is textTools
@@ -1105,7 +1105,8 @@ export class CurrentUserUtils {
tools = CurrentUserUtils.textTools(doc);
break;
}
- tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle, checkResult }) => {
+ tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => {
+ const computed = click ? ComputedField.MakeFunction(click) as any : "transparent";
menuDocList.push(Docs.Create.FontIconDocument({
_nativeWidth: width ? width : 25,
_nativeHeight: 25,
@@ -1116,7 +1117,7 @@ export class CurrentUserUtils {
numBtnType,
numBtnMin,
numBtnMax,
- script,
+ script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
btnType: btnType,
btnList: new List<string>(list),
ignoreClick: ignoreClick,
@@ -1128,10 +1129,10 @@ export class CurrentUserUtils {
title,
switchToggle,
color: Colors.WHITE,
- backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
+ backgroundColor: computed,
_dropAction: "alias",
_removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
- onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined
+ onClick: click ? ScriptField.MakeScript(click) : undefined
}));
});
docList.push(CurrentUserUtils.linearButtonList({
@@ -1141,7 +1142,7 @@ export class CurrentUserUtils {
linearViewExpandable: true,
icon: title,
_height: 30,
- backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
+ // backgroundColor: hidden ? ComputedField.MakeFunction(hidden, { }, { _readOnly_: true }) as any : "transparent",
linearViewIsExpanded: expanded ? !(ComputedField.MakeFunction(expanded) as any) : undefined,
hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
}, menuDocList));
@@ -1153,7 +1154,7 @@ export class CurrentUserUtils {
_height: 25,
icon,
toolTip,
- script,
+ script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
btnType,
btnList: new List<string>(list),
ignoreClick,
@@ -1164,11 +1165,11 @@ export class CurrentUserUtils {
dontUndo: true,
title,
color: Colors.WHITE,
- backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
+ // backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult, {}, {_readOnly_:true}) as any : "transparent",
_dropAction: "alias",
hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
_removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
- onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined
+ onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }, { _readOnly_: false }) : undefined
}));
}
});
@@ -1383,7 +1384,7 @@ export class CurrentUserUtils {
if (response) {
const result: { id: string, email: string, cacheDocumentIds: string } = JSON.parse(response);
Doc.CurrentUserEmail = result.email;
- resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts"));
+ resolvedPorts = JSON.parse(await (await fetch("/resolvedPorts")).text());
DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email);
result.cacheDocumentIds && (await DocServer.GetRefFields(result.cacheDocumentIds.split(";")));
return result;
@@ -1566,7 +1567,7 @@ export class CurrentUserUtils {
@computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
}
-Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
+ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
const copy = Doc.copyDragFactory(dragFactory);
if (copy) {
CollectionDockingView.AddSplit(copy, "right");
@@ -1574,27 +1575,27 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
view && SelectionManager.SelectView(view, false);
}
});
-Scripting.addGlobal(function MySharedDocs() { return Doc.SharingDoc(); },
+ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); },
"document containing all shared Docs");
-Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
+ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
"is Dash in novice mode");
-Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
+ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
"creates a snapshot copy of a dashboard");
-Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
+ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
"creates a new dashboard when called");
-Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
+ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
"creates a new presentation when called");
-Scripting.addGlobal(function createNewFolder() { return MainView.Instance.createNewFolder(); },
+ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); },
"creates a new folder in myFiles when called");
-Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
+ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
"returns all the links to the document or its annotations", "(doc: any)");
-Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); },
+ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); },
"imports files from device directly into the import sidebar");
-Scripting.addGlobal(function shareDashboard(dashboard: Doc) {
+ScriptingGlobals.add(function shareDashboard(dashboard: Doc) {
SharingManager.Instance.open(undefined, dashboard);
},
"opens sharing dialog for Dashboard");
-Scripting.addGlobal(async function removeDashboard(dashboard: Doc) {
+ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
if (dashboards && dashboards.length > 1) {
if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!);
@@ -1602,7 +1603,7 @@ Scripting.addGlobal(async function removeDashboard(dashboard: Doc) {
}
},
"Remove Dashboard from Dashboards");
-Scripting.addGlobal(async function addToDashboards(dashboard: Doc) {
+ScriptingGlobals.add(async function addToDashboards(dashboard: Doc) {
const dashboardAlias = Doc.MakeAlias(dashboard);
const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]);
@@ -1626,7 +1627,7 @@ Scripting.addGlobal(async function addToDashboards(dashboard: Doc) {
/**
* Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard.
*/
-Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
+ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) {
if (dashboard[DataSym] instanceof Doc) {
const allDocs = DocListCast(dashboard["data-all"]);
const onScreenTab = DocListCast(dashboard.data)[0];
@@ -1638,7 +1639,7 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
}
return [];
});
-Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
+ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined;
if (selected && checkParent) {
const parentDoc: Doc = Cast(selected.context, Doc, null);
@@ -1649,11 +1650,11 @@ Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colTyp
else if (selected && !colType && !docType) return false;
else return true;
});
-Scripting.addGlobal(function makeTopLevelFolder() {
+ScriptingGlobals.add(function makeTopLevelFolder() {
const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined };
return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder);
});
-Scripting.addGlobal(function toggleComicMode() {
+ScriptingGlobals.add(function toggleComicMode() {
Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic";
}); \ No newline at end of file
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index a93b2f573..a6dcda4bc 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,4 +1,6 @@
import * as interpreter from "words-to-numbers";
+// @ts-ignore bcz: how are you supposed to include these definitions since dom-speech-recognition isn't a module?
+import type { } from "@types/dom-speech-recognition";
import { Doc, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
import { RichTextField } from "../../fields/RichTextField";
@@ -13,6 +15,7 @@ import { DocumentView } from "../views/nodes/DocumentView";
import { SelectionManager } from "./SelectionManager";
import { UndoManager } from "./UndoManager";
+
/**
* This namespace provides a singleton instance of a manager that
* handles the listening and text-conversion of user speech.
@@ -102,17 +105,17 @@ export namespace DictationManager {
try {
results = await (pendingListen = listenImpl(options));
pendingListen = undefined;
- if (results) {
- Utils.CopyText(results);
- if (overlay) {
- DictationOverlay.Instance.isListening = false;
- const execute = options?.tryExecute;
- DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
- DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
- }
- options?.tryExecute && await DictationManager.Commands.execute(results);
- }
- } catch (e) {
+ // if (results) {
+ // Utils.CopyText(results);
+ // if (overlay) {
+ // DictationOverlay.Instance.isListening = false;
+ // const execute = options?.tryExecute;
+ // DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
+ // DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
+ // }
+ // options?.tryExecute && await DictationManager.Commands.execute(results);
+ // }
+ } catch (e: any) {
console.log(e);
if (overlay) {
DictationOverlay.Instance.isListening = false;
@@ -188,7 +191,7 @@ export namespace DictationManager {
current && sessionResults.push(current);
sessionResults.length && resolve(sessionResults.join(inter || interSession));
} else {
- resolve(current);
+ resolve(current || "");
}
current = undefined;
sessionResults = [];
@@ -222,168 +225,168 @@ export namespace DictationManager {
}
- export namespace Commands {
-
- export const dictationFadeDuration = 2000;
-
- export type IndependentAction = (target: DocumentView) => any | Promise<any>;
- export type IndependentEntry = { action: IndependentAction, restrictTo?: DocumentType[] };
-
- export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise<any>;
- export type DependentEntry = { expression: RegExp, action: DependentAction, restrictTo?: DocumentType[] };
-
- export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value);
- export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry);
-
- export const execute = async (phrase: string) => {
- return UndoManager.RunInBatch(async () => {
- const targets = SelectionManager.Views();
- if (!targets || !targets.length) {
- return;
- }
-
- phrase = phrase.toLowerCase();
- const entry = Independent.get(phrase);
-
- if (entry) {
- let success = false;
- const restrictTo = entry.restrictTo;
- for (const target of targets) {
- if (!restrictTo || validate(target, restrictTo)) {
- await entry.action(target);
- success = true;
- }
- }
- return success;
- }
-
- for (const entry of Dependent) {
- const regex = entry.expression;
- const matches = regex.exec(phrase);
- regex.lastIndex = 0;
- if (matches !== null) {
- let success = false;
- const restrictTo = entry.restrictTo;
- for (const target of targets) {
- if (!restrictTo || validate(target, restrictTo)) {
- await entry.action(target, matches);
- success = true;
- }
- }
- return success;
- }
- }
-
- return false;
- }, "Execute Command");
- };
-
- const ConstructorMap = new Map<DocumentType, CastCtor>([
- [DocumentType.COL, listSpec(Doc)],
- [DocumentType.AUDIO, AudioField],
- [DocumentType.IMG, ImageField],
- [DocumentType.IMPORT, listSpec(Doc)],
- [DocumentType.RTF, "string"]
- ]);
-
- const tryCast = (view: DocumentView, type: DocumentType) => {
- const ctor = ConstructorMap.get(type);
- if (!ctor) {
- return false;
- }
- return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined;
- };
-
- const validate = (target: DocumentView, types: DocumentType[]) => {
- for (const type of types) {
- if (tryCast(target, type)) {
- return true;
- }
- }
- return false;
- };
-
- const interpretNumber = (number: string) => {
- const initial = parseInt(number);
- if (!isNaN(initial)) {
- return initial;
- }
- const converted = interpreter.wordsToNumbers(number, { fuzzy: true });
- if (converted === null) {
- return NaN;
- }
- return typeof converted === "string" ? parseInt(converted) : converted;
- };
-
- const Independent = new Map<string, IndependentEntry>([
-
- ["clear", {
- action: (target: DocumentView) => Doc.GetProto(target.props.Document).data = new List(),
- restrictTo: [DocumentType.COL]
- }],
-
- ["open fields", {
- action: (target: DocumentView) => {
- const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
- target.props.addDocTab(kvp, "add:right");
- }
- }],
-
- ["new outline", {
- action: (target: DocumentView) => {
- const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true });
- const proto = newBox.proto!;
- const prompt = "Press alt + r to start dictating here...";
- const head = 3;
- const anchor = head + prompt.length;
- const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
- proto.data = new RichTextField(proseMirrorState);
- proto.backgroundColor = "#eeffff";
- target.props.addDocTab(newBox, "add:right");
- }
- }]
-
- ]);
-
- const Dependent = new Array<DependentEntry>(
-
- {
- expression: /create (\w+) documents of type (image|nested collection)/g,
- action: (target: DocumentView, matches: RegExpExecArray) => {
- const count = interpretNumber(matches[1]);
- const what = matches[2];
- const dataDoc = Doc.GetProto(target.props.Document);
- const fieldKey = "data";
- if (isNaN(count)) {
- return;
- }
- for (let i = 0; i < count; i++) {
- let created: Doc | undefined;
- switch (what) {
- case "image":
- created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg");
- break;
- case "nested collection":
- created = Docs.Create.FreeformDocument([], {});
- break;
- }
- created && Doc.AddDocToList(dataDoc, fieldKey, created);
- }
- },
- restrictTo: [DocumentType.COL]
- },
-
- {
- expression: /view as (freeform|stacking|masonry|schema|tree)/g,
- action: (target: DocumentView, matches: RegExpExecArray) => {
- const mode = matches[1];
- mode && (target.props.Document._viewType = mode);
- },
- restrictTo: [DocumentType.COL]
- }
-
- );
-
- }
+ // export namespace Commands {
+
+ // export const dictationFadeDuration = 2000;
+
+ // export type IndependentAction = (target: DocumentView) => any | Promise<any>;
+ // export type IndependentEntry = { action: IndependentAction, restrictTo?: DocumentType[] };
+
+ // export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise<any>;
+ // export type DependentEntry = { expression: RegExp, action: DependentAction, restrictTo?: DocumentType[] };
+
+ // export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value);
+ // export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry);
+
+ // export const execute = async (phrase: string) => {
+ // return UndoManager.RunInBatch(async () => {
+ // const targets = SelectionManager.Views();
+ // if (!targets || !targets.length) {
+ // return;
+ // }
+
+ // phrase = phrase.toLowerCase();
+ // const entry = Independent.get(phrase);
+
+ // if (entry) {
+ // let success = false;
+ // const restrictTo = entry.restrictTo;
+ // for (const target of targets) {
+ // if (!restrictTo || validate(target, restrictTo)) {
+ // await entry.action(target);
+ // success = true;
+ // }
+ // }
+ // return success;
+ // }
+
+ // for (const entry of Dependent) {
+ // const regex = entry.expression;
+ // const matches = regex.exec(phrase);
+ // regex.lastIndex = 0;
+ // if (matches !== null) {
+ // let success = false;
+ // const restrictTo = entry.restrictTo;
+ // for (const target of targets) {
+ // if (!restrictTo || validate(target, restrictTo)) {
+ // await entry.action(target, matches);
+ // success = true;
+ // }
+ // }
+ // return success;
+ // }
+ // }
+
+ // return false;
+ // }, "Execute Command");
+ // };
+
+ // const ConstructorMap = new Map<DocumentType, CastCtor>([
+ // [DocumentType.COL, listSpec(Doc)],
+ // [DocumentType.AUDIO, AudioField],
+ // [DocumentType.IMG, ImageField],
+ // [DocumentType.IMPORT, listSpec(Doc)],
+ // [DocumentType.RTF, "string"]
+ // ]);
+
+ // const tryCast = (view: DocumentView, type: DocumentType) => {
+ // const ctor = ConstructorMap.get(type);
+ // if (!ctor) {
+ // return false;
+ // }
+ // return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined;
+ // };
+
+ // const validate = (target: DocumentView, types: DocumentType[]) => {
+ // for (const type of types) {
+ // if (tryCast(target, type)) {
+ // return true;
+ // }
+ // }
+ // return false;
+ // };
+
+ // const interpretNumber = (number: string) => {
+ // const initial = parseInt(number);
+ // if (!isNaN(initial)) {
+ // return initial;
+ // }
+ // const converted = interpreter.wordsToNumbers(number, { fuzzy: true });
+ // if (converted === null) {
+ // return NaN;
+ // }
+ // return typeof converted === "string" ? parseInt(converted) : converted;
+ // };
+
+ // const Independent = new Map<string, IndependentEntry>([
+
+ // ["clear", {
+ // action: (target: DocumentView) => Doc.GetProto(target.props.Document).data = new List(),
+ // restrictTo: [DocumentType.COL]
+ // }],
+
+ // ["open fields", {
+ // action: (target: DocumentView) => {
+ // const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
+ // target.props.addDocTab(kvp, "add:right");
+ // }
+ // }],
+
+ // ["new outline", {
+ // action: (target: DocumentView) => {
+ // const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true });
+ // const proto = newBox.proto!;
+ // const prompt = "Press alt + r to start dictating here...";
+ // const head = 3;
+ // const anchor = head + prompt.length;
+ // const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
+ // proto.data = new RichTextField(proseMirrorState);
+ // proto.backgroundColor = "#eeffff";
+ // target.props.addDocTab(newBox, "add:right");
+ // }
+ // }]
+
+ // ]);
+
+ // const Dependent = new Array<DependentEntry>(
+
+ // {
+ // expression: /create (\w+) documents of type (image|nested collection)/g,
+ // action: (target: DocumentView, matches: RegExpExecArray) => {
+ // const count = interpretNumber(matches[1]);
+ // const what = matches[2];
+ // const dataDoc = Doc.GetProto(target.props.Document);
+ // const fieldKey = "data";
+ // if (isNaN(count)) {
+ // return;
+ // }
+ // for (let i = 0; i < count; i++) {
+ // let created: Doc | undefined;
+ // switch (what) {
+ // case "image":
+ // created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg");
+ // break;
+ // case "nested collection":
+ // created = Docs.Create.FreeformDocument([], {});
+ // break;
+ // }
+ // created && Doc.AddDocToList(dataDoc, fieldKey, created);
+ // }
+ // },
+ // restrictTo: [DocumentType.COL]
+ // },
+
+ // {
+ // expression: /view as (freeform|stacking|masonry|schema|tree)/g,
+ // action: (target: DocumentView, matches: RegExpExecArray) => {
+ // const mode = matches[1];
+ // mode && (target.props.Document._viewType = mode);
+ // },
+ // restrictTo: [DocumentType.COL]
+ // }
+
+ // );
+
+ // }
} \ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 66b6a1e44..0a00ab6e0 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -9,7 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView';
import { LightboxView } from '../views/LightboxView';
import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
-import { Scripting } from './Scripting';
+import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
export class DocumentManager {
@@ -198,7 +198,7 @@ export class DocumentManager {
originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
new Promise<ViewAdjustment>(res => {
focusAndFinish(didFocus);
- res();
+ res(ViewAdjustment.doNothing);
})
});
} else {
@@ -227,7 +227,7 @@ export class DocumentManager {
willZoom, afterFocus: (didFocus: boolean) =>
new Promise<ViewAdjustment>(res => {
!noSelect && focusAndFinish(didFocus);
- res();
+ res(ViewAdjustment.doNothing);
})
}); // focus on the target in the context
} else if (delay > 1500) {
@@ -252,7 +252,7 @@ export class DocumentManager {
}
}
-Scripting.addGlobal(function DocFocusOrOpen(doc: any) {
+ScriptingGlobals.add(function DocFocusOrOpen(doc: any) {
const dv = DocumentManager.Instance.getDocumentView(doc);
if (dv && dv.props.Document === doc) {
dv.props.focus(doc, { willZoom: true });
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ae3fa3170..c9c499fff 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -499,7 +499,7 @@ export namespace DragManager {
screenX: e.screenX,
screenY: e.screenY,
detail: e.detail,
- view: e.view ? e.view : new Window,
+ view: e.view ? e.view : new Window as any,
nativeEvent: new DragEvent("dashDragAutoScroll"),
currentTarget: target,
target: target,
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 32817eefd..082b6d8bd 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -7,7 +7,7 @@ import { Docs } from "../documents/Documents";
import { ScriptField, ComputedField } from "../../fields/ScriptField";
import { RichTextField } from "../../fields/RichTextField";
import { ImageField } from "../../fields/URLField";
-import { Scripting } from "./Scripting";
+import { ScriptingGlobals } from "./ScriptingGlobals";
import { listSpec } from "../../fields/Schema";
//
@@ -81,5 +81,5 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
data.droppedDocuments[i] = dbox;
});
}
-Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); },
+ScriptingGlobals.add(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); },
"converts the dropped data to buttons", "(dragData: any)"); \ No newline at end of file
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index cbe36b401..e6f75a7db 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -103,8 +103,7 @@ export namespace HistoryUtil {
return undefined;
}
const parser = requiredFields[required];
- let value = opts[required];
- value = parse(parser, value);
+ const value = parse(parser, opts[required]);
if (value !== null && value !== undefined) {
current[required] = value;
}
@@ -114,8 +113,7 @@ export namespace HistoryUtil {
continue;
}
const parser = optionalFields[opt];
- let value = opts[opt];
- value = parse(parser, value);
+ const value = parse(parser, opts[opt]);
if (value !== undefined) {
current[opt] = value;
}
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 925b74efa..39e9251a5 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -1,9 +1,8 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { BatchedArray } from "array-batcher";
-import "fs";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import * as path from 'path';
+import { extname } from "path";
import Measure, { ContentRect } from "react-measure";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
@@ -86,7 +85,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
for (let i = 0; i < files.length; i++) {
const file = files.item(i);
if (file && !unsupported.includes(file.type)) {
- const ext = path.extname(file.name).toLowerCase();
+ const ext = extname(file.name).toLowerCase();
if (AcceptableMedia.imageFormats.includes(ext)) {
validated.push(file);
}
@@ -120,7 +119,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
}
const { accessPaths, exifData } = result;
const path = Utils.prepend(accessPaths.agnostic.client);
- const document = await DocUtils.DocumentFromType(type, path, { _width: 300, title: name });
+ const document = type && await DocUtils.DocumentFromType(type, path, { _width: 300 });
const { data, error } = exifData;
if (document) {
Doc.GetProto(document).exif = error || Doc.Get.FromJson({ data });
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 62b13e2c6..df2c02a8d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -194,7 +194,7 @@ export class LinkManager {
afterFocus: (didFocus: boolean) => {
finished?.();
res(ViewAdjustment.resetView);
- return new Promise<ViewAdjustment>(res2 => res2());
+ return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
}
});
} else {
diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts
index 94806a7ba..42a6493ea 100644
--- a/src/client/util/ScriptManager.ts
+++ b/src/client/util/ScriptManager.ts
@@ -1,9 +1,9 @@
import { Doc, DocListCast } from "../../fields/Doc";
import { List } from "../../fields/List";
-import { Scripting } from "./Scripting";
-import { StrCast, Cast } from "../../fields/Types";
import { listSpec } from "../../fields/Schema";
+import { Cast, StrCast } from "../../fields/Types";
import { Docs } from "../documents/Documents";
+import { ScriptingGlobals } from "./ScriptingGlobals";
export class ScriptManager {
@@ -44,7 +44,7 @@ export class ScriptManager {
public deleteScript(scriptDoc: Doc): boolean {
if (scriptDoc.name) {
- Scripting.removeGlobal(StrCast(scriptDoc.name));
+ ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name));
}
const scriptList = this.getAllScripts();
const index = scriptList.indexOf(scriptDoc);
@@ -60,7 +60,7 @@ export class ScriptManager {
public static addScriptToGlobals(scriptDoc: Doc): void {
- Scripting.removeGlobal(StrCast(scriptDoc.name));
+ ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name));
const params = Cast(scriptDoc["data-params"], listSpec("string"), []);
const paramNames = params.reduce((o: string, p: string) => {
@@ -86,9 +86,9 @@ export class ScriptManager {
});
if (parameters === "(") {
- Scripting.addGlobal(f, StrCast(scriptDoc.description));
+ ScriptingGlobals.add(f, StrCast(scriptDoc.description));
} else {
- Scripting.addGlobal(f, StrCast(scriptDoc.description), parameters);
+ ScriptingGlobals.add(f, StrCast(scriptDoc.description), parameters);
}
}
} \ No newline at end of file
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 40b94024e..3b0a47b54 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,16 +1,15 @@
-import * as ts from "typescript";
-export { ts };
-
// export const ts = (window as any).ts;
-
// // @ts-ignore
// import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts'
// // @ts-ignore
// import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
-
// @ts-ignore
import * as typescriptlib from '!!raw-loader!./type_decls.d';
-import { Doc, Field } from '../../fields/Doc';
+import * as ts from "typescript";
+import { Doc, Field } from "../../fields/Doc";
+import { scriptingGlobals, ScriptingGlobals } from "./ScriptingGlobals";
+export { ts };
+
export interface ScriptSuccess {
success: true;
@@ -47,98 +46,6 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is
return false;
}
-export namespace Scripting {
- export function addGlobal(global: { name: string }): void;
- export function addGlobal(name: string, global: any): void;
-
- export function addGlobal(global: { name: string }, decription?: string, params?: string): void;
-
- export function addGlobal(first: any, second?: any, third?: string) {
- let n: any;
- let obj: any;
-
- if (second !== undefined) {
- if (typeof first === "string") {
- n = first;
- obj = second;
- } else {
- obj = first;
- n = first.name;
- _scriptingDescriptions[n] = second;
- if (third !== undefined) {
- _scriptingParams[n] = third;
- }
- }
- } else if (first && typeof first.name === "string") {
- n = first.name;
- obj = first;
- } else {
- throw new Error("Must either register an object with a name, or give a name and an object");
- }
- if (n === undefined || n === "undefined") {
- return false;
- } else if (_scriptingGlobals.hasOwnProperty(n)) {
- throw new Error(`Global with name ${n} is already registered, choose another name`);
- }
- _scriptingGlobals[n] = obj;
- }
-
- export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) {
- return { ..._scriptingGlobals, ...(globals || {}) };
- }
-
- export function setScriptingGlobals(globals: { [key: string]: any }) {
- scriptingGlobals = globals;
- }
-
- export function removeGlobal(name: string) {
- if (getGlobals().includes(name)) {
- delete _scriptingGlobals[name];
- if (_scriptingDescriptions[name]) {
- delete _scriptingDescriptions[name];
- }
- if (_scriptingParams[name]) {
- delete _scriptingParams[name];
- }
- return true;
- }
- return false;
- }
-
- export function resetScriptingGlobals() {
- scriptingGlobals = _scriptingGlobals;
- }
-
- // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]);
- export function printNodeType(node: any, indentation = "") {
- console.log(indentation + ts.SyntaxKind[node.kind]);
- }
-
- export function getGlobals() {
- return Object.keys(_scriptingGlobals);
- }
-
- export function getGlobalObj() {
- return _scriptingGlobals;
- }
-
- export function getDescriptions() {
- return _scriptingDescriptions;
- }
-
- export function getParameters() {
- return _scriptingParams;
- }
-}
-
-export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
- Scripting.addGlobal(constructor);
-}
-
-export const _scriptingGlobals: { [name: string]: any } = {};
-let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
-const _scriptingDescriptions: { [name: string]: any } = {};
-const _scriptingParams: { [name: string]: any } = {};
function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error);
@@ -273,7 +180,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
if (options.params && !options.params.this) options.params.this = Doc.name;
if (options.params && !options.params.self) options.params.self = Doc.name;
if (options.globals) {
- Scripting.setScriptingGlobals(options.globals);
+ ScriptingGlobals.setScriptingGlobals(options.globals);
}
const host = new ScriptingCompilerHost;
if (options.traverser) {
@@ -331,9 +238,9 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const result = Run(outputText, paramNames, diagnostics, script, options);
if (options.globals) {
- Scripting.resetScriptingGlobals();
+ ScriptingGlobals.resetScriptingGlobals();
}
return result;
}
-Scripting.addGlobal(CompileScript); \ No newline at end of file
+ScriptingGlobals.add(CompileScript);
diff --git a/src/client/util/ScriptingGlobals.ts b/src/client/util/ScriptingGlobals.ts
new file mode 100644
index 000000000..f151acd81
--- /dev/null
+++ b/src/client/util/ScriptingGlobals.ts
@@ -0,0 +1,81 @@
+
+import * as ts from "typescript";
+export { ts };
+
+export namespace ScriptingGlobals {
+ export function add(global: { name: string }): void;
+ export function add(name: string, global: any): void;
+ export function add(global: { name: string }, decription?: string, params?: string): void;
+ export function add(first: any, second?: any, third?: string) {
+ let n: any;
+ let obj: any;
+
+ if (second !== undefined) {
+ if (typeof first === "string") {
+ n = first;
+ obj = second;
+ } else {
+ obj = first;
+ n = first.name;
+ _scriptingDescriptions[n] = second;
+ if (third !== undefined) {
+ _scriptingParams[n] = third;
+ }
+ }
+ } else if (first && typeof first.name === "string") {
+ n = first.name;
+ obj = first;
+ } else {
+ throw new Error("Must either register an object with a name, or give a name and an object");
+ }
+ if (n === undefined || n === "undefined") {
+ return false;
+ } else if (_scriptingGlobals.hasOwnProperty(n)) {
+ throw new Error(`Global with name ${n} is already registered, choose another name`);
+ }
+ _scriptingGlobals[n] = obj;
+ }
+ export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) {
+ return { ..._scriptingGlobals, ...(globals || {}) };
+ }
+
+ export function setScriptingGlobals(globals: { [key: string]: any }) {
+ scriptingGlobals = globals;
+ }
+
+ export function removeGlobal(name: string) {
+ if (getGlobals().includes(name)) {
+ delete _scriptingGlobals[name];
+ if (_scriptingDescriptions[name]) {
+ delete _scriptingDescriptions[name];
+ }
+ if (_scriptingParams[name]) {
+ delete _scriptingParams[name];
+ }
+ return true;
+ }
+ return false;
+ }
+
+ export function resetScriptingGlobals() { scriptingGlobals = _scriptingGlobals; }
+
+ // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]);
+ export function printNodeType(node: any, indentation = "") { console.log(indentation + ts.SyntaxKind[node.kind]); }
+
+ export function getGlobals() { return Object.keys(_scriptingGlobals); }
+
+ export function getGlobalObj() { return _scriptingGlobals; }
+
+ export function getDescriptions() { return _scriptingDescriptions; }
+
+ export function getParameters() { return _scriptingParams; }
+}
+
+export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
+ ScriptingGlobals.add(constructor);
+}
+
+const _scriptingGlobals: { [name: string]: any } = {};
+export let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
+const _scriptingDescriptions: { [name: string]: any } = {};
+const _scriptingParams: { [name: string]: any } = {}; \ No newline at end of file
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 44e44d335..d1f1a0099 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,5 +1,4 @@
import { observable, action, runInAction } from "mobx";
-import 'source-map-support/register';
import { Without } from "../../Utils";
function getBatchName(target: any, key: string | symbol): string {
@@ -107,7 +106,7 @@ export namespace UndoManager {
}
export function FilterBatches(fieldTypes: string[]) {
const fieldCounts: { [key: string]: number } = {};
- const lastStack = UndoManager.undoStack.lastElement();
+ const lastStack = UndoManager.undoStack.slice(-1)[0];//.lastElement();
if (lastStack) {
lastStack.forEach(ev => fieldTypes.includes(ev.prop) && (fieldCounts[ev.prop] = (fieldCounts[ev.prop] || 0) + 1));
const fieldCount2: { [key: string]: number } = {};
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index b9772fd57..2e6ea1faa 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -20,10 +20,10 @@ export interface DocComponentProps {
LayoutTemplate?: () => Opt<Doc>;
LayoutTemplateString?: string;
}
-export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) {
+export function DocComponent<P extends DocComponentProps>() {
class Component extends Touchable<P> {
//TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
- @computed get Document(): T { return schemaCtor(this.props.Document); }
+ @computed get Document() { return this.props.Document; }
// This is the "The Document" -- it encapsulates, data, layout, and any templates
@computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
// This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
@@ -48,7 +48,7 @@ interface ViewBoxBaseProps {
renderDepth: number;
rootSelected: (outsideReaction?: boolean) => boolean;
}
-export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor: (doc: Doc) => T) {
+export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
class Component extends Touchable<P> {
//TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
//@computed get Document(): T { return schemaCtor(this.props.Document); }
@@ -92,17 +92,17 @@ export interface ViewBoxAnnotatableProps {
renderDepth: number;
isAnnotationOverlay?: boolean;
}
-export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
+export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
class Component extends Touchable<P> {
@observable _annotationKeySuffix = () => "annotations";
@observable _isAnyChildContentActive = false;
//TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
- @computed get Document(): T { return schemaCtor(this.props.Document); }
+ @computed get Document() { return this.props.Document; }
// This is the "The Document" -- it encapsulates, data, layout, and any templates
@computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
// This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
- @computed get layoutDoc() { return schemaCtor(Doc.Layout(this.props.Document)); }
+ @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
// This is the data part of a document -- ie, the data that is constant across all views of the document
@computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d85709f31..d0b5bfe46 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -22,13 +22,13 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
import { KeyManager } from './GlobalKeyHandler';
+import { InkingStroke } from './InkingStroke';
import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
import { DocumentView } from "./nodes/DocumentView";
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import React = require("react");
-import { InkingStroke } from './InkingStroke';
-import e = require('express');
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
@observer
export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> {
@@ -148,7 +148,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
pageY: e.pageY,
preventDefault: emptyFunction,
button: 0
- }, [SelectionManager.Views().lastElement().rootDoc]);
+ }, [SelectionManager.Views().slice(-1)[0].rootDoc]);
return true;
}, emptyFunction, this.onMaximizeClick, false, false);
}
@@ -233,7 +233,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
this._snapX = e.pageX;
this._snapY = e.pageY;
- SelectionManager.Views().forEach(docView => this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) }));
+ const ffviewSet = new Set<CollectionFreeFormView>();
+ SelectionManager.Views().forEach(docView => {
+ const ffview = docView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ ffview && ffviewSet.add(ffview);
+ this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) });
+ });
+ Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false));
}
onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
@@ -395,10 +401,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
.forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
Doc.GetProto(doc).data = new InkField(inkPts.map(ipt => // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
- ({
- X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
- Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
- })));
+ ({
+ X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
+ Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
+ })));
Doc.SetNativeWidth(doc, undefined);
Doc.SetNativeHeight(doc, undefined);
});
@@ -424,7 +430,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
render() {
const bounds = this.Bounds;
- const seldoc = SelectionManager.Views().lastElement();
+ const seldoc = SelectionManager.Views().slice(-1)[0];
if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
return (null);
}
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 130e71924..05e5b7d5f 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -13,7 +13,7 @@ import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { DocUtils } from "../documents/Documents";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { InteractionUtils } from "../util/InteractionUtils";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
import { SelectionManager } from "../util/SelectionManager";
import { Transform } from "../util/Transform";
import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
@@ -943,11 +943,11 @@ export enum ToolglassTools {
None = "none",
}
-Scripting.addGlobal("GestureOverlay", GestureOverlay);
-Scripting.addGlobal(function setToolglass(tool: any) {
+ScriptingGlobals.add("GestureOverlay", GestureOverlay);
+ScriptingGlobals.add(function setToolglass(tool: any) {
runInAction(() => GestureOverlay.Instance.Tool = tool);
});
-Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
+ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
runInAction(() => {
GestureOverlay.Instance.SavedColor = ActiveInkColor();
SetActiveInkColor(color);
@@ -959,12 +959,12 @@ Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStar
SetActiveDash(dash);
});
});
-Scripting.addGlobal(function resetPen() {
+ScriptingGlobals.add(function resetPen() {
runInAction(() => {
SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? "2");
});
}, "resets the pen tool");
-Scripting.addGlobal(function createText(text: any, x: any, y: any) {
+ScriptingGlobals.add(function createText(text: any, x: any, y: any) {
GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text);
}, "creates a text document with inputted text and coordinates", "(text: any, x: any, y: any)");
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index 24f796105..a7f511ab4 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -51,6 +51,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this.props.inkCtrlPoints.length;
const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"));
const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex;
+ if (!wasSelected) InkStrokeProperties.Instance._currentPoint = -1;
const origInk = this.props.inkCtrlPoints.slice();
setupMoveUpEvents(this, e,
action((e: PointerEvent, down: number[], delta: number[]) => {
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 9dbd97c16..40fe6aa68 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -24,9 +24,7 @@ import React = require("react");
import { action, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import { Doc, WidthSym } from "../../fields/Doc";
-import { documentSchema } from "../../fields/documentSchemas";
import { InkData, InkField, InkTool } from "../../fields/InkField";
-import { makeInterface } from "../../fields/Schema";
import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
import { TraceMobx } from "../../fields/util";
import { OmitKeys, returnFalse, setupMoveUpEvents } from "../../Utils";
@@ -46,11 +44,8 @@ import { FieldView, FieldViewProps } from "./nodes/FieldView";
import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
import Color = require("color");
-type InkDocument = makeInterface<[typeof documentSchema]>;
-const InkDocument = makeInterface(documentSchema);
-
@observer
-export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) {
+export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
static readonly MaskDim = 50000; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big)
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
public static IsClosed(inkData: InkData) {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 7553c8118..8560ccb29 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,13 +1,15 @@
-import { MainView } from "./MainView";
-import { Docs } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import * as ReactDOM from 'react-dom';
+// if ((module as any).hot) {
+// (module as any).hot.accept();
+// }
+
import * as React from 'react';
-import { DocServer } from "../DocServer";
+import * as ReactDOM from 'react-dom';
import { AssignAllExtensions } from "../../extensions/General/Extensions";
-import { Networking } from "../Network";
-import { CollectionView } from "./collections/CollectionView";
+import { Docs } from "../documents/Documents";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { LinkManager } from "../util/LinkManager";
+import { CollectionView } from "./collections/CollectionView";
+import { MainView } from "./MainView";
AssignAllExtensions();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1478da1f7..8c0795881 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,7 +5,6 @@ import * as fa from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import "normalize.css";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
@@ -24,7 +23,7 @@ import { DocumentManager } from '../util/DocumentManager';
import { GroupManager } from '../util/GroupManager';
import { HistoryUtil } from '../util/History';
import { Hypothesis } from '../util/HypothesisUtils';
-import { Scripting } from '../util/Scripting';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { SelectionManager } from '../util/SelectionManager';
import { ColorScheme, SettingsManager } from '../util/SettingsManager';
import { SharingManager } from '../util/SharingManager';
@@ -45,7 +44,6 @@ import { GestureOverlay } from './GestureOverlay';
import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
import { Colors } from './global/globalEnums';
import { KeyManager } from './GlobalKeyHandler';
-import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
import { LinkMenu } from './linking/LinkMenu';
import "./MainView.scss";
@@ -165,28 +163,28 @@ export class MainView extends React.Component {
}
}
- library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
- fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
- fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
- fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
- fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
- fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
- fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
- fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
- fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
- fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
- fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
- fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
- fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
- fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
- fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
- fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
- fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
- fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
- fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
- fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
- fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
- fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt);
+ library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
+ fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
+ fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
+ fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
+ fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
+ fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
+ fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
+ fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
+ fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
+ fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
+ fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
+ fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
+ fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
+ fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
+ fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
+ fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
+ fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
+ fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
+ fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
+ fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
+ fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
+ fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]);
this.initAuthenticationRouters();
}
@@ -696,4 +694,4 @@ export class MainView extends React.Component {
}
}
-Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); \ No newline at end of file
+ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); \ No newline at end of file
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 7cf388872..0f51cf9b2 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -5,17 +5,17 @@ import ReactLoading from 'react-loading';
import { Doc } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { Cast, NumCast } from "../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils, emptyPath } from "../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../Utils";
+import { DocUtils } from "../documents/Documents";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { DragManager } from "../util/DragManager";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
import { Transform } from "../util/Transform";
import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
import { DocumentView } from "./nodes/DocumentView";
import './OverlayView.scss';
import { ScriptingRepl } from './ScriptingRepl';
import { DefaultStyleProvider } from "./StyleProvider";
-import { DocUtils } from "../documents/Documents";
export type OverlayDisposer = () => void;
@@ -226,6 +226,6 @@ export class OverlayView extends React.Component {
}
}
// bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error
-Scripting.addGlobal(function addOverlayWindow(type: string, options: OverlayElementOptions) {
+ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) {
OverlayView.Instance.addWindow(<ScriptingRepl />, options);
}); \ No newline at end of file
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index f9dab9f82..dd6448654 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -18,6 +18,10 @@ import React = require("react");
import { Colors } from "./global/globalEnums";
import { CollectionFreeFormView } from "./collections/collectionFreeForm";
import { DocumentManager } from "../util/DocumentManager";
+import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
+import { VideoBox } from "./nodes/VideoBox";
+import { Id } from "../../fields/FieldSymbols";
+import { ImageField } from "../../fields/URLField";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -92,6 +96,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@computed get groupButton() {
return this.propertyToggleBtn("Group", "isGroup", on => `Display collection as a Group`, on => "object-group", (dv, doc) => { doc.isGroup = !doc.isGroup; doc.forceActive = doc.isGroup; });
}
+ @computed get freezeThumb() {
+ return this.propertyToggleBtn("Freeze\Thumb", "_thumb-frozen", on => `${on ? "Freeze" : "Unfreeze"} thumbnail`, on => "arrows-alt-h", (dv, doc) => {
+ if (doc["thumb-frozen"]) doc["thumb-frozen"] = undefined;
+ else {
+ document.body.focus(); // so that we can access the clipboard without an error
+ setTimeout(() =>
+ pasteImageBitmap((data: any, error: any) => {
+ error && console.log(error);
+ data && VideoBox.convertDataUri(data, doc[Id] + "-thumb-frozen", true).then(
+ returnedfilename => doc["thumb-frozen"] = new ImageField(returnedfilename));
+ }));
+ }
+ });
+ }
@computed get snapButton() {
return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true);
}
@@ -222,6 +240,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
{toggle(this.dictationButton, { display: isNovice ? "none" : "" })}
{toggle(this.onClickButton)}
{toggle(this.fitWidthButton)}
+ {toggle(this.freezeThumb)}
{toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? "none" : "" })}
{toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })}
{toggle(this.maskButton, { display: !isInk ? "none" : "" })}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 3e7b0bd0f..47a5cd07e 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,6 +1,6 @@
import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAnchor, faArrowRight } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Checkbox, Tooltip } from "@material-ui/core";
import { intersection } from "lodash";
import { action, autorun, computed, Lambda, observable } from "mobx";
@@ -17,6 +17,7 @@ import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, retu
import { DocumentType } from "../documents/DocumentTypes";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { DocumentManager } from "../util/DocumentManager";
+import { LinkManager } from "../util/LinkManager";
import { SelectionManager } from "../util/SelectionManager";
import { SharingManager } from "../util/SharingManager";
import { Transform } from "../util/Transform";
@@ -27,12 +28,12 @@ import { EditableView } from "./EditableView";
import { InkStrokeProperties } from "./InkStrokeProperties";
import { DocumentView, StyleProviderFunc } from "./nodes/DocumentView";
import { KeyValueBox } from "./nodes/KeyValueBox";
-import { PresBox } from "./nodes/trails/PresBox";
import { PropertiesButtons } from "./PropertiesButtons";
import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
import "./PropertiesView.scss";
import { DefaultStyleProvider } from "./StyleProvider";
-import { LinkManager } from "../util/LinkManager";
+import { PresBox } from "./nodes/trails";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -1301,7 +1302,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={this.toggleAnchor} onClick={e => e.stopPropagation()}
className="propertiesButton"
>
- <FontAwesomeIcon className="fa-icon" icon={faAnchor} size="lg" />
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
</button>
</div>
<div className="propertiesView-input inline" id="propertiesView-displayArrow">
@@ -1311,7 +1312,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={this.toggleArrow} onClick={e => e.stopPropagation()}
className="propertiesButton"
>
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight} size="lg" />
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
</button>
</div>
</div>
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index e2b5d8dc3..b7ea124b9 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -1,17 +1,17 @@
-import * as React from "react";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { observable, action } from "mobx";
-
-import "./ScriptBox.scss";
-import { OverlayView } from "./OverlayView";
-import { DocumentIconContainer } from "./nodes/DocumentIcon";
-import { Opt, Doc } from "../../fields/Doc";
-import { emptyFunction } from "../../Utils";
-import { ScriptCast } from "../../fields/Types";
-import { CompileScript } from "../util/Scripting";
+import * as React from "react";
+import { Doc, Opt } from "../../fields/Doc";
import { ScriptField } from "../../fields/ScriptField";
+import { ScriptCast } from "../../fields/Types";
+import { emptyFunction } from "../../Utils";
import { DragManager } from "../util/DragManager";
+import { CompileScript } from "../util/Scripting";
import { EditableView } from "./EditableView";
+import { DocumentIconContainer } from "./nodes/DocumentIcon";
+import { OverlayView } from "./OverlayView";
+import "./ScriptBox.scss";
+
export interface ScriptBoxProps {
onSave: (text: string, onError: (error: string) => void) => void;
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index c598b2861..4ed6da24a 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -3,7 +3,8 @@ import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { DocumentManager } from '../util/DocumentManager';
-import { CompileScript, Scripting, Transformer, ts } from '../util/Scripting';
+import { CompileScript, Transformer, ts } from '../util/Scripting';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { DocumentIconContainer } from './nodes/DocumentIcon';
import { OverlayView } from './OverlayView';
import './ScriptingRepl.scss';
@@ -81,7 +82,7 @@ export class ScriptingRepl extends React.Component {
transformer: context => {
const knownVars: { [name: string]: number } = {};
const usedDocuments: number[] = [];
- Scripting.getGlobals().forEach(global => knownVars[global] = 1);
+ ScriptingGlobals.getGlobals().forEach((global: any) => knownVars[global] = 1);
return root => {
function visit(node: ts.Node) {
let skip = false;
@@ -124,7 +125,7 @@ export class ScriptingRepl extends React.Component {
case "Enter": {
const docGlobals: { [name: string]: any } = {};
DocumentManager.Instance.DocumentViews.forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document);
- const globals = Scripting.makeMutableGlobalsCopy(docGlobals);
+ const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer(), globals });
if (!script.compiled) {
this.commands.push({ command: this.commandString, result: script.errors });
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 8ee673115..a2f23ee09 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -7,7 +7,7 @@ import { Doc, Opt, StrListCast } from "../../fields/Doc";
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
-import { lightOrDark, DashColor } from '../../Utils';
+import { DashColor, lightOrDark } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
import { ColorScheme } from '../util/SettingsManager';
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index ff3f92364..b3a24e031 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -5,9 +5,9 @@ import { List } from "../../fields/List";
import { ScriptField } from "../../fields/ScriptField";
import { Cast, StrCast } from "../../fields/Types";
import { TraceMobx } from "../../fields/util";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
import { Transform } from "../util/Transform";
import { undoBatch } from "../util/UndoManager";
import { CollectionTreeView } from "./collections/CollectionTreeView";
@@ -164,7 +164,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
}
}
-Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) {
+ScriptingGlobals.add(function switchView(doc: Doc, template: Doc | undefined) {
if (template?.dragFactory) {
template = Cast(template.dragFactory, Doc, null);
}
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index 82b0218bf..92d3e2bed 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -1,18 +1,17 @@
-import * as React from "react";
-import "./Keyframe.scss";
-import "./Timeline.scss";
-import "../global/globalCssVariables.scss";
+import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
-import { Cast, NumCast } from "../../../fields/Types";
+import * as React from "react";
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
import { List } from "../../../fields/List";
-import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../fields/Schema";
-import { Transform } from "../../util/Transform";
-import { TimelineMenu } from "./TimelineMenu";
+import { createSchema, defaultSpec, listSpec, makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast } from "../../../fields/Types";
import { Docs } from "../../documents/Documents";
+import { Transform } from "../../util/Transform";
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { emptyPath } from "../../../Utils";
+import "../global/globalCssVariables.scss";
+import "./Keyframe.scss";
+import "./Timeline.scss";
+import { TimelineMenu } from "./TimelineMenu";
/**
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index c7e62c15d..e80ba6f36 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -13,6 +13,7 @@ import { TimelineOverview } from "./TimelineOverview";
import { Track } from "./Track";
import clamp from "../../util/clamp";
import { DocumentType } from "../../documents/DocumentTypes";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
/**
* Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are
@@ -346,9 +347,9 @@ export class Timeline extends React.Component<FieldViewProps> {
return (
<div key="timeline_toolbox" className="timeline-toolbox" style={{ height: `${size}px` }}>
<div className="playbackControls">
- <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
- <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
- <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+ <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+ <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+ <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
</div>
<div className="grid-box overview-tool">
<div className="overview-box">
@@ -506,7 +507,7 @@ export class Timeline extends React.Component<FieldViewProps> {
{this.children.map(doc => <div style={{ height: `${(this._titleHeight)}px` }} className="datapane" onPointerOver={() => { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}><p>{doc.title}</p></div>)}
</div>
<div key="timeline_resize" onPointerDown={this.onResizeDown}>
- <FontAwesomeIcon className="resize" icon={faGripLines} />
+ <FontAwesomeIcon className="resize" icon={faGripLines as IconLookup} />
</div>
</div>
</div>
diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx
index 53ca9acad..aa422c092 100644
--- a/src/client/views/animationtimeline/TimelineMenu.tsx
+++ b/src/client/views/animationtimeline/TimelineMenu.tsx
@@ -5,6 +5,7 @@ import "./TimelineMenu.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons";
import { Utils } from "../../../Utils";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
@observer
@@ -41,7 +42,7 @@ export class TimelineMenu extends React.Component {
if (type === "input") {
const inputRef = React.createRef<HTMLInputElement>();
let text = "";
- this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => {
+ this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard as IconLookup} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => {
e.stopPropagation();
text = e.target.value;
}} onKeyDown={(e) => {
@@ -52,7 +53,7 @@ export class TimelineMenu extends React.Component {
}
}} /></div>);
} else if (type === "button") {
- this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine} size="lg" /><p className="timeline-menu-desc" onClick={(e) => {
+ this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine as IconLookup} size="lg" /><p className="timeline-menu-desc" onClick={(e) => {
e.preventDefault();
e.stopPropagation();
event(e);
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 3c66faf0c..01f41869e 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -3,23 +3,17 @@ import { computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc } from '../../../fields/Doc';
-import { collectionSchema, documentSchema } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
-import { makeInterface } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { OmitKeys, returnFalse, Utils } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { DocumentView } from '../nodes/DocumentView';
+import { StyleProp } from '../StyleProvider';
import "./CollectionCarousel3DView.scss";
import { CollectionSubView } from './CollectionSubView';
-import { StyleProp } from '../StyleProvider';
-
-type Carousel3DDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
-const Carousel3DDocument = makeInterface(documentSchema, collectionSchema);
@observer
-export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocument) {
+export class CollectionCarousel3DView extends CollectionSubView() {
@computed get scrollSpeed() {
return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
}
@@ -28,7 +22,7 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
componentWillUnmount() { this._dropDisposer?.(); }
- protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
this._dropDisposer?.();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 6c2c27e8e..467c2893f 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -3,8 +3,6 @@ import { computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Opt } from '../../../fields/Doc';
-import { collectionSchema, documentSchema } from '../../../fields/documentSchemas';
-import { makeInterface } from '../../../fields/Schema';
import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { OmitKeys, returnFalse } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
@@ -12,18 +10,15 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { StyleProp } from '../StyleProvider';
import "./CollectionCarouselView.scss";
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-
-type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
-const CarouselDocument = makeInterface(documentSchema, collectionSchema);
+import { CollectionSubView } from './CollectionSubView';
@observer
-export class CollectionCarouselView extends CollectionSubView(CarouselDocument) {
+export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
componentWillUnmount() { this._dropDisposer?.(); }
- protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
this._dropDisposer?.();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f543d924d..9e8374605 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -4,31 +4,31 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from "mo
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import * as GoldenLayout from "../../../client/goldenLayout";
-import { Doc, DocListCast, Opt, DocListCastAsync, DataSym } from "../../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { inheritParentAcls } from '../../../fields/util';
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { DragManager } from "../../util/DragManager";
import { InteractionUtils } from '../../util/InteractionUtils';
-import { Scripting } from '../../util/Scripting';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { LightboxView } from '../LightboxView';
import "./CollectionDockingView.scss";
import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
import { CollectionViewType } from './CollectionView';
import { TabDocView } from './TabDocView';
import React = require("react");
-import { DocumentType } from '../../documents/DocumentTypes';
-import { listSpec } from '../../../fields/Schema';
-import { LightboxView } from '../LightboxView';
-import { inheritParentAcls } from '../../../fields/util';
const _global = (window /* browser */ || global /* node */) as any;
@observer
-export class CollectionDockingView extends CollectionSubView(doc => doc) {
+export class CollectionDockingView extends CollectionSubView() {
@observable public static Instance: CollectionDockingView;
public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) {
return {
@@ -468,8 +468,8 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
}
}
-Scripting.addGlobal(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); },
+ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); },
"opens up document in a lightbox", "(doc: any)");
-Scripting.addGlobal(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
+ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
"opens up document in tab on right side of the screen", "(doc: any)");
-Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); \ No newline at end of file
+ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index fc713e19e..f548e6b0e 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -20,7 +20,7 @@ import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { DragManager } from "../../util/DragManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -1286,7 +1286,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
);
}
}
-Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) {
+ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
const dataField = doc[Doc.LayoutFieldKey(doc)];
const childDocs = DocListCast(dataField);
const currentFrame = Cast(doc._currentFrame, "number", null);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index bc1407c53..0a336c544 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -13,7 +13,7 @@ import { CollectionSubView } from "./CollectionSubView";
import React = require("react");
@observer
-export class CollectionPileView extends CollectionSubView(doc => doc) {
+export class CollectionPileView extends CollectionSubView() {
_originalChrome: any = "";
_disposers: { [name: string]: IReactionDisposer } = {};
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 89da6692a..e09e9aa35 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -5,14 +5,14 @@ import {
IReactionDisposer,
observable,
reaction,
- runInAction,
+ runInAction
} from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
import { Doc, DocListCast } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
import {
@@ -22,33 +22,30 @@ import {
returnFalse,
returnOne,
setupMoveUpEvents,
- StopEvent,
- returnTrue,
+ StopEvent
} from "../../../Utils";
import { Docs } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
+import { DragManager } from "../../util/DragManager";
import { LinkManager } from "../../util/LinkManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
import { SelectionManager } from "../../util/SelectionManager";
+import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { AudioWaveform } from "../AudioWaveform";
import { CollectionSubView } from "../collections/CollectionSubView";
+import { Colors } from "../global/globalEnums";
import { LightboxView } from "../LightboxView";
import {
DocAfterFocusFunc,
DocFocusFunc,
DocumentView,
- DocumentViewProps,
+ DocumentViewProps
} from "../nodes/DocumentView";
import { LabelBox } from "../nodes/LabelBox";
import "./CollectionStackedTimeline.scss";
-import { Colors } from "../global/globalEnums";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { DragManager } from "../../util/DragManager";
-type PanZoomDocument = makeInterface<[]>;
-const PanZoomDocument = makeInterface();
export type CollectionStackedTimelineProps = {
duration: number;
Play: () => void;
@@ -70,10 +67,7 @@ export type CollectionStackedTimelineProps = {
};
@observer
-export class CollectionStackedTimeline extends CollectionSubView<
- PanZoomDocument,
- CollectionStackedTimelineProps
->(PanZoomDocument) {
+export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
@observable static SelectingRegion: CollectionStackedTimeline | undefined =
undefined;
static RangeScript: ScriptField;
@@ -721,7 +715,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
DocListCast(this.props.mark.links).length &&
time > NumCast(this.props.mark[this.props.startTag]) &&
time < NumCast(this.props.mark[this.props.endTag]) &&
- this._lastTimecode < NumCast(this.props.mark[this.props.startTag])
+ this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5
) {
LinkManager.FollowLink(
undefined,
@@ -851,31 +845,31 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
{inner.view}
{!inner.anchor.view ||
!SelectionManager.IsSelected(inner.anchor.view) ? null : (
- <>
- <div
- key="left"
- className="collectionStackedTimeline-left-resizer"
- onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
- />
- <div
- key="right"
- className="collectionStackedTimeline-resizer"
- onPointerDown={(e) =>
- this.onAnchorDown(e, this.props.mark, false)
- }
- />
- </>
- )}
+ <>
+ <div
+ key="left"
+ className="collectionStackedTimeline-left-resizer"
+ onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
+ />
+ <div
+ key="right"
+ className="collectionStackedTimeline-resizer"
+ onPointerDown={(e) =>
+ this.onAnchorDown(e, this.props.mark, false)
+ }
+ />
+ </>
+ )}
</>
);
}
}
-Scripting.addGlobal(function formatToTime(time: number): any {
+ScriptingGlobals.add(function formatToTime(time: number): any {
return formatTime(time);
});
-Scripting.addGlobal(function min(num1: number, num2: number): number {
+ScriptingGlobals.add(function min(num1: number, num2: number): number {
return Math.min(num1, num2);
});
-Scripting.addGlobal(function max(num1: number, num2: number): number {
+ScriptingGlobals.add(function max(num1: number, num2: number): number {
return Math.max(num1, num2);
}); \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index cdc680a08..8634ea139 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,16 +3,15 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CursorProperty } from "csstype";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../fields/Doc";
-import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } 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, returnZero, setupMoveUpEvents, smoothScroll, Utils, returnTrue, returnEmptyDoclist, returnEmptyFilter } from "../../../Utils";
-import { DocUtils, Docs } from "../../documents/Documents";
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
+import { Docs, DocUtils } from "../../documents/Documents";
import { DragManager, dropActionType } from "../../util/DragManager";
import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
@@ -23,18 +22,14 @@ import { EditableView } from "../EditableView";
import { LightboxView } from "../LightboxView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp, DefaultStyleProvider } from "../StyleProvider";
+import { StyleProp } from "../StyleProvider";
import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
import "./CollectionStackingView.scss";
import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
import { CollectionSubView } from "./CollectionSubView";
import { CollectionViewType } from "./CollectionView";
-import { FontIconBox } from "../nodes/button/FontIconBox";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
const _global = (window /* browser */ || global /* node */) as any;
-type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
-const StackingDocument = makeInterface(collectionSchema, documentSchema);
export type collectionStackingViewProps = {
chromeHidden?: boolean;
@@ -44,7 +39,7 @@ export type collectionStackingViewProps = {
};
@observer
-export class CollectionStackingView extends CollectionSubView<StackingDocument, Partial<collectionStackingViewProps>>(StackingDocument) {
+export class CollectionStackingView extends CollectionSubView<Partial<collectionStackingViewProps>>() {
_masonryGridRef: HTMLDivElement | null = null;
_draggerRef = React.createRef<HTMLDivElement>();
_pivotFieldDisposer?: IReactionDisposer;
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
index c5c3f96e8..c025e94a8 100644
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -6,7 +6,7 @@ import "./CollectionStaffView.scss";
import { observer } from "mobx-react";
@observer
-export class CollectionStaffView extends CollectionSubView(doc => doc) {
+export class CollectionStaffView extends CollectionSubView() {
private _reactionDisposer: IReactionDisposer | undefined;
@observable private _staves = NumCast(this.props.Document.staves);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index fc1bcb8b9..42e157396 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -10,14 +10,14 @@ import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
import { GestureUtils } from "../../../pen-gestures/GestureUtils";
import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
import { DocServer } from "../../DocServer";
-import { Networking } from "../../Network";
import { ImageUtils } from "../../util/Import & Export/ImageUtils";
import { InteractionUtils } from "../../util/InteractionUtils";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { DocComponent } from "../DocComponent";
import React = require("react");
-import * as rp from 'request-promise';
import ReactLoading from 'react-loading';
+import * as rp from 'request-promise';
+import { Networking } from "../../Network";
export interface SubCollectionViewProps extends CollectionViewProps {
@@ -25,15 +25,15 @@ export interface SubCollectionViewProps extends CollectionViewProps {
isAnyChildContentActive: () => boolean;
}
-export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
- class CollectionSubView extends DocComponent<X & SubCollectionViewProps, T>(schemaCtor) {
+export function CollectionSubView<X>(moreProps?: X) {
+ class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
private gestureDisposer?: GestureUtils.GestureEventDisposer;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _mainCont?: HTMLDivElement;
@observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
@observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
- protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
this.dropDisposer?.();
this.gestureDisposer?.();
this._multiTouchDisposer?.();
@@ -446,6 +446,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options));
}
if (generatedDocuments.length) {
+ // Creating a dash document
const isFreeformView = this.props.Document._viewType === CollectionViewType.Freeform;
const set = !isFreeformView ? generatedDocuments :
generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 292dfd77c..d6398fda5 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,17 +1,17 @@
import { toUpper } from "lodash";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocCastAsync, Opt, StrListCast } from "../../../fields/Doc";
+import { Doc, Opt, StrListCast } from "../../../fields/Doc";
import { List } from "../../../fields/List";
import { ObjectField } from "../../../fields/ObjectField";
import { RichTextField } from "../../../fields/RichTextField";
import { listSpec } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, returnEmptyString } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { EditableView } from "../EditableView";
@@ -20,13 +20,10 @@ import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngi
import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTimeView.scss";
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
import React = require("react");
@observer
-export class CollectionTimeView extends CollectionSubView(doc => doc) {
+export class CollectionTimeView extends CollectionSubView() {
_changing = false;
@observable _layoutEngine = "pivot";
@observable _collapsed: boolean = false;
@@ -236,7 +233,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
}
}
-Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
+ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
pivotDoc["_prevDocFilter" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField);
pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docRangeFilters as ObjectField);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ea077ea40..e84517f40 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,11 +3,11 @@ import { observer } from "mobx-react";
import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
-import { Document, listSpec } from '../../../fields/Schema';
+import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnOne } from '../../../Utils';
+import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { DocumentManager } from '../../util/DocumentManager';
@@ -40,7 +40,7 @@ export type collectionTreeViewProps = {
};
@observer
-export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) {
+export class CollectionTreeView extends CollectionSubView<Partial<collectionTreeViewProps>>() {
private _treedropDisposer?: DragManager.DragDropDisposer;
private _mainEle?: HTMLDivElement;
private _titleRef?: HTMLDivElement | HTMLInputElement | null;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 681a15e3d..ee2c28b5f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,15 +1,14 @@
-import { computed, observable, runInAction, action } from 'mobx';
+import { computed, observable, runInAction } from 'mobx';
import { observer } from "mobx-react";
import * as React from 'react';
import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
-import { Doc, DocListCast, StrListCast } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { ObjectField } from '../../../fields/ObjectField';
-import { makeInterface } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
+import { returnEmptyString } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { BranchCreate, BranchTask } from '../../documents/Gitlike';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -33,10 +32,7 @@ import { CollectionStackingView } from './CollectionStackingView';
import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from "./CollectionTreeView";
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import './CollectionView.scss';
-import { returnEmptyString } from '../../../Utils';
-import { InkTool } from '../../../fields/InkField';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
@@ -84,11 +80,8 @@ export interface CollectionViewProps extends FieldViewProps {
childClickScript?: ScriptField;
childDoubleClickScript?: ScriptField;
}
-
-type CollectionDocument = makeInterface<[typeof documentSchema]>;
-const CollectionDocument = makeInterface(documentSchema);
@observer
-export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument) {
+export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
@observable private static _safeMode = false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index aeda71d01..e2ea81392 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,13 +4,12 @@ import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
import { DateField } from "../../../../fields/DateField";
import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
-import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas";
import { Id } from "../../../../fields/FieldSymbols";
import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField";
import { List } from "../../../../fields/List";
import { ObjectField } from "../../../../fields/ObjectField";
import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema";
+import { createSchema, listSpec } from "../../../../fields/Schema";
import { ScriptField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
import { TraceMobx } from "../../../../fields/util";
@@ -41,7 +40,6 @@ import { LightboxView } from "../../LightboxView";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { pageSchema } from "../../nodes/ImageBox";
import { PresBox } from "../../nodes/trails/PresBox";
import { StyleLayers, StyleProp } from "../../StyleProvider";
import { CollectionDockingView } from "../CollectionDockingView";
@@ -67,8 +65,6 @@ export const panZoomSchema = createSchema({
scrollHeight: "number" // this will be set when the collection is an annotation overlay for a PDF/Webpage
});
-type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>;
-const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema);
export type collectionFreeformViewProps = {
annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
viewDefDivClick?: ScriptField;
@@ -81,7 +77,7 @@ export type collectionFreeformViewProps = {
};
@observer
-export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, Partial<collectionFreeformViewProps>>(PanZoomDocument) {
+export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
private _lastNudge: any;
@@ -95,6 +91,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
private _clusterDistance: number = 75;
private _hitCluster: number = -1;
private _disposers: { [name: string]: IReactionDisposer } = {};
+ private _renderCutoffData = observable.map<string, boolean>();
private _layoutPoolData = observable.map<string, PoolData>();
private _layoutSizeData = observable.map<string, { width?: number, height?: number }>();
private _cachedPool: Map<string, PoolData> = new Map();
@@ -109,6 +106,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@observable _viewTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _hLines: number[] | undefined;
@observable _vLines: number[] | undefined;
+ @observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown.
@observable _pullCoords: number[] = [0, 0];
@observable _pullDirection: string = "";
@observable _showAnimTimeline = false;
@@ -163,7 +161,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.layoutDoc._panY = vals.bounds.cy;
this.layoutDoc._viewScale = vals.scale;
}
- freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined;
+ freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
reverseNativeScaling = () => this.fitToContent ? true : false;
panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
@@ -205,7 +203,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}
if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
- CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document._currentFrame, true);
+ CollectionFreeFormDocumentView.setupKeyframes(newBoxes, NumCast(this.Document._currentFrame), true);
}
}
return retVal;
@@ -235,12 +233,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
if (this.Document._currentFrame !== undefined) {
- CollectionFreeFormDocumentView.setupKeyframes([d], this.Document._currentFrame, false);
+ CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
vals.x = x + (vals.x || 0) - dropPos[0];
vals.y = y + (vals.y || 0) - dropPos[1];
vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined;
- CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, vals);
+ CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals);
} else {
d.x = x + NumCast(d.x) - dropPos[0];
d.y = y + NumCast(d.y) - dropPos[1];
@@ -635,7 +633,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, 0, true);
+ this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
}
@@ -967,10 +965,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
.filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! }));
if (measuredDocs.length) {
const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) => // computes range of content
- ({
- xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
- yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
- })
+ ({
+ xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
+ yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
+ })
, {
xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }
@@ -1045,11 +1043,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if (state.type === "doc" && this.Document._panX !== undefined && this.Document._panY !== undefined) {
const init = state.initializers![this.Document[Id]];
if (!init) {
- state.initializers![this.Document[Id]] = { panX: this.Document._panX, panY: this.Document._panY };
+ state.initializers![this.Document[Id]] = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) };
HistoryUtil.pushState(state);
} else if (init.panX !== this.Document._panX || init.panY !== this.Document._panY) {
- init.panX = this.Document._panX;
- init.panY = this.Document._panY;
+ init.panX = NumCast(this.Document._panX);
+ init.panY = NumCast(this.Document._panY);
HistoryUtil.pushState(state);
}
}
@@ -1144,7 +1142,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
isContentActive = () => this.props.isSelected() || this.props.isContentActive();
- getChildDocView(entry: PoolData) {
+ getChildDocView(entry: PoolData, renderIndex: number) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
@@ -1153,6 +1151,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
Document={childLayout}
renderDepth={this.props.renderDepth + 1}
replica={entry.replica}
+ renderIndex={renderIndex}
+ renderCutoffProvider={this.renderCutoffProvider}
ContainingCollectionView={this.props.CollectionView}
ContainingCollectionDoc={this.props.Document}
CollectionFreeFormView={this}
@@ -1213,7 +1213,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const { z, color, zIndex } = params.pair.layout;
const { x, y, opacity } = this.Document._currentFrame === undefined ?
{ x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } :
- CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document._currentFrame);
+ CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
return {
x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
transition: StrCast(layoutDoc.dataTransition), opacity: this._keyframeEditing ? 1 : Cast(opacity, "number", null),
@@ -1256,6 +1256,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}
+
+ renderCutoffProvider = computedFn(function renderCutoffProvider(this: any, doc: Doc) {
+ return !this._renderCutoffData.get(doc[Id] + "");
+ }.bind(this));
+
+
childPositionProviderUnmemoized = (doc: Doc, replica: string) => {
return this._layoutPoolData.get(doc[Id] + (replica || ""));
}
@@ -1300,6 +1306,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
+ @observable _numLoaded = 1;
get doLayoutComputation() {
const { newPool, computedElementData } = this.doInternalLayoutComputation;
const array = Array.from(newPool.entries());
@@ -1318,9 +1325,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this._cachedPool.clear();
Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
const elements = computedElementData.slice();
- Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry =>
+ Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach((entry, i) =>
elements.push({
- ele: this.getChildDocView(entry[1]),
+ ele: this.getChildDocView(entry[1], i),
bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
}));
@@ -1369,35 +1376,38 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
componentDidMount() {
super.componentDidMount?.();
this.props.setContentView?.(this);
- this._disposers.layoutComputation = reaction(() => this.doLayoutComputation,
- (elements) => this._layoutElements = elements || [],
- { fireImmediately: true, name: "doLayout" });
-
- this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
-
- this._disposers.groupBounds = reaction(() => {
- if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
- const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
- return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
- }
- return undefined;
- },
- (cbounds) => {
- if (cbounds) {
- const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
- const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
- const pbounds = {
- x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
- r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
- };
- this.layoutDoc._width = (pbounds.r - pbounds.x);
- this.layoutDoc._height = (pbounds.b - pbounds.y);
- this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
- this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
- this.layoutDoc.x = pbounds.x;
- this.layoutDoc.y = pbounds.y;
+ setTimeout(action(() => {
+ this._firstRender = false;
+ this._disposers.layoutComputation = reaction(() => this.doLayoutComputation,
+ (elements) => this._layoutElements = elements || [],
+ { fireImmediately: true, name: "doLayout" });
+
+ this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
+
+ this._disposers.groupBounds = reaction(() => {
+ if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
+ const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
+ return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
}
- }, { fireImmediately: true });
+ return undefined;
+ },
+ (cbounds) => {
+ if (cbounds) {
+ const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
+ const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
+ const pbounds = {
+ x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
+ r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
+ };
+ this.layoutDoc._width = (pbounds.r - pbounds.x);
+ this.layoutDoc._height = (pbounds.b - pbounds.y);
+ this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
+ this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
+ this.layoutDoc.x = pbounds.x;
+ this.layoutDoc.y = pbounds.y;
+ }
+ }, { fireImmediately: true });
+ }));
}
componentWillUnmount() {
@@ -1449,8 +1459,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20;
const dim = Math.ceil(Math.sqrt(docs.length));
docs.forEach((doc, i) => {
- doc.x = (this.Document._panX || 0) + (i % dim) * width - width * dim / 2;
- doc.y = (this.Document._panY || 0) + Math.floor(i / dim) * height - height * dim / 2;
+ doc.x = NumCast(this.Document._panX) + (i % dim) * width - width * dim / 2;
+ doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - height * dim / 2;
});
}
@@ -1557,58 +1567,35 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
e.stopPropagation();
}
+ incrementalRender = action(() => {
+ if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
+ const unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
+ const loadIncrement = 5;
+ for (var i = 0; i < Math.min(unrendered.length, loadIncrement); i++) {
+ this._renderCutoffData.set(unrendered[i][Id] + "", true);
+ }
+ }
+ this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
+ });
+
children = () => {
+ this.incrementalRender();
const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
- return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />];
- }
-
- chooseGridSpace = (gridSpace: number): number => {
- if (!this.zoomScaling()) return 50;
- const divisions = this.props.PanelWidth() / this.zoomScaling() / gridSpace + 3;
- return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
- }
-
- @computed get backgroundGrid() {
- const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-spacing"], 50));
- const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.panX() % gridSpace - gridSpace) * this.zoomScaling();
- const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.panY() % gridSpace - gridSpace) * this.zoomScaling();
- const renderGridSpace = gridSpace * this.zoomScaling();
- const w = this.props.PanelWidth() + 2 * renderGridSpace;
- const h = this.props.PanelHeight() + 2 * renderGridSpace;
- const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)";
- return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
- ref={(el) => {
- const ctx = el?.getContext('2d');
- if (ctx) {
- const Cx = this.cachedCenteringShiftX % renderGridSpace;
- const Cy = this.cachedCenteringShiftY % renderGridSpace;
- ctx.lineWidth = Math.min(1, Math.max(0.5, this.zoomScaling()));
- ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
- ctx.clearRect(0, 0, w, h);
- if (ctx) {
- ctx.strokeStyle = strokeStyle;
- ctx.beginPath();
- for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
- ctx.moveTo(x, Cy - h);
- ctx.lineTo(x, Cy + h);
- }
- for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
- ctx.moveTo(Cx - w, y);
- ctx.lineTo(Cx + w, y);
- }
- ctx.stroke();
- }
- }
- }} />;
+ return [
+ ...children,
+ ...this.views,
+ <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+ ];
}
@computed get placeholder() {
- return <div className="collectionfreeformview-placeholder" style={{ background: this.Document.backgroundColor }}>
+ return <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
<span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span>
</div>;
}
@computed get marqueeView() {
+ TraceMobx();
return <MarqueeView
{...this.props}
ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
@@ -1623,7 +1610,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
<div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
- {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)}
+ {this.layoutDoc._backgroundGridShow ?
+ <CollectionFreeFormBackgroundGrid
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
+ panX={this.panX}
+ panY={this.panY}
+ zoomScaling={this.zoomScaling}
+ layoutDoc={this.layoutDoc}
+ isAnnotationOverlay={this.isAnnotationOverlay}
+ cachedCenteringShiftX={this.cachedCenteringShiftX}
+ cachedCenteringShiftY={this.cachedCenteringShiftY}
+ /> : (null)}
<CollectionFreeFormViewPannableContents
isAnnotationOverlay={this.isAnnotationOverlay}
isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
@@ -1642,7 +1640,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
@computed get contentScaling() {
- if (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent) return 0;
+ if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0;
const nw = this.nativeWidth;
const nh = this.nativeHeight;
const hscale = nh ? this.props.PanelHeight() / nh : 1;
@@ -1675,9 +1673,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.backgroundEvents ? "all" : this.props.pointerEvents as any,
transform: `scale(${this.contentScaling || 1})`,
width: `${100 / (this.contentScaling || 1)}%`,
- height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
+ height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
}}>
- {this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ?
+ {this._firstRender || (this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0) ?
this.placeholder : this.marqueeView}
{this.props.noOverlay ? (null) : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
@@ -1854,4 +1852,59 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
{this.zoomProgressivize}
</div>;
}
+}
+
+interface CollectionFreeFormViewBackgroundGridProps {
+ panX: () => number;
+ panY: () => number;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
+ isAnnotationOverlay?: boolean;
+ zoomScaling: () => number;
+ layoutDoc: Doc;
+ cachedCenteringShiftX: number;
+ cachedCenteringShiftY: number;
+}
+@observer
+class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
+
+
+ chooseGridSpace = (gridSpace: number): number => {
+ if (!this.props.zoomScaling()) return 50;
+ const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace + 3;
+ return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
+ }
+ render() {
+ const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc["_backgroundGrid-spacing"], 50));
+ const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.props.panX() % gridSpace - gridSpace) * this.props.zoomScaling();
+ const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.props.panY() % gridSpace - gridSpace) * this.props.zoomScaling();
+ const renderGridSpace = gridSpace * this.props.zoomScaling();
+ const w = this.props.PanelWidth() + 2 * renderGridSpace;
+ const h = this.props.PanelHeight() + 2 * renderGridSpace;
+ const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)";
+ return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
+ ref={(el) => {
+ const ctx = el?.getContext('2d');
+ if (ctx) {
+ const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
+ const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
+ ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
+ ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
+ ctx.clearRect(0, 0, w, h);
+ if (ctx) {
+ ctx.strokeStyle = strokeStyle;
+ ctx.beginPath();
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
+ ctx.moveTo(x, Cy - h);
+ ctx.lineTo(x, Cy + h);
+ }
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.moveTo(Cx - w, y);
+ ctx.lineTo(Cx + w, y);
+ }
+ ctx.stroke();
+ }
+ }
+ }} />;
+ }
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 08da682bb..b10b0912f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -30,6 +30,9 @@ import "./MarqueeView.scss";
import React = require("react");
import { StyleLayers } from "../../StyleProvider";
import { TreeView } from "../TreeView";
+import { VideoBox } from "../../nodes/VideoBox";
+import { ImageField, WebField } from "../../../../fields/URLField";
+import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -134,17 +137,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
})();
e.stopPropagation();
} 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);
- // }
- // });
- // e.stopPropagation();
-
+ document.body.focus(); // so that we can access the clipboard without an error
+ setTimeout(() =>
+ pasteImageBitmap((data: any, error: any) => {
+ error && console.log(error);
+ data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => {
+ this.props.Document["thumb-frozen"] = new ImageField(returnedfilename);
+ });
+ }));
+ } else if (e.key === "s" && e.ctrlKey) {
e.preventDefault();
const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!;
slide.x = x;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index b0030471d..58ea7410d 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -2,9 +2,7 @@ import { action, computed, Lambda, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from "react";
import { Doc, Opt } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
import { Id } from '../../../../fields/FieldSymbols';
-import { makeInterface } from '../../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
@@ -20,11 +18,8 @@ import { CollectionSubView } from '../CollectionSubView';
import "./CollectionGridView.scss";
import Grid, { Layout } from "./Grid";
-type GridSchema = makeInterface<[typeof documentSchema]>;
-const GridSchema = makeInterface(documentSchema);
-
@observer
-export class CollectionGridView extends CollectionSubView(GridSchema) {
+export class CollectionGridView extends CollectionSubView() {
private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index d67122eff..44762dbe3 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -4,9 +4,7 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mo
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
import { Id } from '../../../../fields/FieldSymbols';
-import { makeInterface } from '../../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
import { DragManager } from '../../../util/DragManager';
@@ -21,9 +19,6 @@ import { CollectionViewType } from '../CollectionView';
import "./CollectionLinearView.scss";
-type LinearDocument = makeInterface<[typeof documentSchema,]>;
-const LinearDocument = makeInterface(documentSchema);
-
/**
* CollectionLinearView is the class for rendering the horizontal collection
* of documents, it useful for horizontal menus. It can either be expandable
@@ -33,7 +28,7 @@ const LinearDocument = makeInterface(documentSchema);
* - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
*/
@observer
-export class CollectionLinearView extends CollectionSubView(LinearDocument) {
+export class CollectionLinearView extends CollectionSubView() {
@observable public addMenuToggle = React.createRef<HTMLInputElement>();
@observable private _selectedIndex = -1;
private _dropDisposer?: DragManager.DragDropDisposer;
@@ -74,7 +69,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
{ fireImmediately: true }
);
}
- protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
this._dropDisposer && this._dropDisposer();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index ec1cbadd5..2bdf92417 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -2,11 +2,9 @@ import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from "react";
import { Doc } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
import { List } from '../../../../fields/List';
-import { makeInterface } from '../../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
+import { emptyFunction, returnFalse } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -16,8 +14,6 @@ import "./CollectionMulticolumnView.scss";
import ResizeBar from './MulticolumnResizer';
import WidthLabel from './MulticolumnWidthLabel';
-type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
-const MulticolumnDocument = makeInterface(documentSchema);
interface WidthSpecifier {
magnitude: number;
@@ -38,7 +34,7 @@ const resolvedUnits = Object.values(DimUnit);
const resizerWidth = 8;
@observer
-export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) {
+export class CollectionMulticolumnView extends CollectionSubView() {
/**
* @returns the list of layout documents whose width unit is
@@ -51,7 +47,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
@computed
private get minimumDim() {
- return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
+ const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude);
+ return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1;
}
/**
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index a2d51e2e7..7e2b83230 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -2,11 +2,9 @@ import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from "react";
import { Doc } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
import { List } from '../../../../fields/List';
-import { makeInterface } from '../../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
+import { emptyFunction, returnFalse } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -16,9 +14,6 @@ import "./CollectionMultirowView.scss";
import HeightLabel from './MultirowHeightLabel';
import ResizeBar from './MultirowResizer';
-type MultirowDocument = makeInterface<[typeof documentSchema]>;
-const MultirowDocument = makeInterface(documentSchema);
-
interface HeightSpecifier {
magnitude: number;
unit: string;
@@ -38,7 +33,7 @@ const resolvedUnits = Object.values(DimUnit);
const resizerHeight = 8;
@observer
-export class CollectionMultirowView extends CollectionSubView(MultirowDocument) {
+export class CollectionMultirowView extends CollectionSubView() {
/**
* @returns the list of layout documents whose width unit is
@@ -51,7 +46,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
@computed
private get minimumDim() {
- return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
+ const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude);
+ return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1;
}
/**
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index a439a7998..c2bb3b3ac 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -2,19 +2,18 @@ import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
+import { extname } from "path";
import DatePicker from "react-datepicker";
-import "react-datepicker/dist/react-datepicker.css";
import { CellInfo } from "react-table";
-import "react-table/react-table.css";
import { DateField } from "../../../../fields/DateField";
import { Doc, DocListCast, Field, Opt } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
import { List } from "../../../../fields/List";
import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
import { ComputedField } from "../../../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { BoolCast, Cast, DateCast, FieldValue, StrCast } from "../../../../fields/Types";
import { ImageField } from "../../../../fields/URLField";
-import { Utils, emptyFunction } from "../../../../Utils";
+import { emptyFunction, Utils } from "../../../../Utils";
import { Docs } from "../../../documents/Documents";
import { DocumentType } from "../../../documents/DocumentTypes";
import { DocumentManager } from "../../../util/DocumentManager";
@@ -29,9 +28,8 @@ import { EditableView } from "../../EditableView";
import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
import { DocumentIconContainer } from "../../nodes/DocumentIcon";
import { OverlayView } from "../../OverlayView";
-import "./CollectionSchemaView.scss";
import { CollectionView } from "../CollectionView";
-const path = require('path');
+import "./CollectionSchemaView.scss";
// intialize cell properties
export interface CellProps {
@@ -495,8 +493,8 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell {
if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href;//Why is this here — good question
- const ext = path.extname(url.href); // the extension of the file
- return url.href.replace(ext, "_o" + path.extname(url.href));
+ const ext = extname(url.href);
+ return url.href.replace(ext, "_o" + ext);
}
render() {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index b89246489..8b73351d5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -4,7 +4,6 @@ import { action, computed, observable, untracked, trace } from "mobx";
import { observer } from "mobx-react";
import Measure from "react-measure";
import { Resize } from "react-table";
-import "react-table/react-table.css";
import { Doc, Opt } from "../../../../fields/Doc";
import { List } from "../../../../fields/List";
import { listSpec } from "../../../../fields/Schema";
@@ -47,7 +46,7 @@ const columnTypes: Map<string, ColumnType> = new Map([
]);
@observer
-export class CollectionSchemaView extends CollectionSubView(doc => doc) {
+export class CollectionSchemaView extends CollectionSubView() {
private _previewCont?: HTMLDivElement;
@observable _previewDoc: Doc | undefined = undefined;
@@ -147,43 +146,43 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
const anyType = <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Any)}>
<FontAwesomeIcon icon={"align-justify"} size="sm" />
- Any
- </div>;
+ Any
+ </div>;
const numType = <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Number)}>
<FontAwesomeIcon icon={"hashtag"} size="sm" />
- Number
- </div>;
+ Number
+ </div>;
const textType = <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.String)}>
<FontAwesomeIcon icon={"font"} size="sm" />
Text
- </div>;
+ </div>;
const boolType = <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
<FontAwesomeIcon icon={"check-square"} size="sm" />
Checkbox
- </div>;
+ </div>;
const listType = <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.List)}>
<FontAwesomeIcon icon={"list-ul"} size="sm" />
List
- </div>;
+ </div>;
const docType = <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
<FontAwesomeIcon icon={"file"} size="sm" />
Document
- </div>;
+ </div>;
const imageType = <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Image)}>
<FontAwesomeIcon icon={"image"} size="sm" />
Image
- </div>;
+ </div>;
const dateType = <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Date)}>
<FontAwesomeIcon icon={"calendar"} size="sm" />
- Date
- </div>;
+ Date
+ </div>;
const allColumnTypes = <div className="columnMenu-types">
@@ -557,7 +556,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
style={{
overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white",
pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined,
- width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
+ width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
}} >
<div className="collectionSchemaView-tableContainer"
style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 2219345f6..605481ddf 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -4,7 +4,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, trace } from "mobx";
import { observer } from "mobx-react";
import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
-import "react-table/react-table.css";
import { DateField } from "../../../../fields/DateField";
import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index c79828470..93377f1dc 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -6,13 +6,11 @@ import {
IReactionDisposer,
observable,
reaction,
- runInAction,
+ runInAction
} from "mobx";
import { observer } from "mobx-react";
import { DateField } from "../../../fields/DateField";
import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { makeInterface } from "../../../fields/Schema";
import { ComputedField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
import { AudioField, nullAudio } from "../../../fields/URLField";
@@ -26,26 +24,18 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import {
ViewBoxAnnotatableComponent,
- ViewBoxAnnotatableProps,
+ ViewBoxAnnotatableProps
} from "../DocComponent";
+import { Colors } from "../global/globalEnums";
import "./AudioBox.scss";
import { FieldView, FieldViewProps } from "./FieldView";
import { LinkDocPreview } from "./LinkDocPreview";
-import { faLessThan } from "@fortawesome/free-solid-svg-icons";
-import { Colors } from "../global/globalEnums";
declare class MediaRecorder {
constructor(e: any); // whatever MediaRecorder has
}
-
-type AudioDocument = makeInterface<[typeof documentSchema]>;
-const AudioDocument = makeInterface(documentSchema);
-
@observer
-export class AudioBox extends ViewBoxAnnotatableComponent<
- ViewBoxAnnotatableProps & FieldViewProps,
- AudioDocument
->(AudioDocument) {
+export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(AudioBox, fieldKey);
}
@@ -70,7 +60,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<
@observable static _scrubTime = 0;
@observable _markerEnd: number = 0;
@observable _position: number = 0;
- @observable _waveHeight: Opt<number> = this.layoutDoc._height;
+ @observable _waveHeight: Opt<number> = NumCast(this.layoutDoc._height);
@observable _paused: boolean = false;
@observable _trimming: boolean = false;
@observable _trimStart: number = NumCast(this.layoutDoc.clipStart) ? NumCast(this.layoutDoc.clipStart) : 0;
@@ -310,7 +300,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<
);
DocUtils.ActiveRecordings.push(this);
this._recorder.ondataavailable = async (e: any) => {
+ console.log("Data available", e);
const [{ result }] = await Networking.UploadFilesToServer(e.data);
+ console.log("Data result", result);
if (!(result instanceof Error)) {
this.props.Document[this.props.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
}
@@ -336,8 +328,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<
(this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") +
" play when link is selected",
event: () =>
- (this.layoutDoc.dontAutoPlayFollowedLinks =
- !this.layoutDoc.dontAutoPlayFollowedLinks),
+ (this.layoutDoc.dontAutoPlayFollowedLinks =
+ !this.layoutDoc.dontAutoPlayFollowedLinks),
icon: "expand-arrows-alt",
});
funcs.push({
@@ -637,77 +629,77 @@ export class AudioBox extends ViewBoxAnnotatableComponent<
</div>
</div>
) : (
- <div
- className={`audiobox-record${interactive}`}
- style={{ backgroundColor: Colors.DARK_GRAY }}
- >
- <FontAwesomeIcon icon="microphone" />
+ <div
+ className={`audiobox-record${interactive}`}
+ style={{ backgroundColor: Colors.DARK_GRAY }}
+ >
+ <FontAwesomeIcon icon="microphone" />
RECORD
- </div>
- )}
+ </div>
+ )}
</div>
) : (
+ <div
+ className="audiobox-controls"
+ style={{
+ pointerEvents:
+ this._isAnyChildContentActive || this.props.isContentActive()
+ ? "all"
+ : "none",
+ }}
+ >
+ <div className="audiobox-dictation" />
<div
- className="audiobox-controls"
- style={{
- pointerEvents:
- this._isAnyChildContentActive || this.props.isContentActive()
- ? "all"
- : "none",
- }}
+ className="audiobox-player"
+ style={{ height: `${AudioBox.heightPercent}%` }}
>
- <div className="audiobox-dictation" />
<div
- className="audiobox-player"
- style={{ height: `${AudioBox.heightPercent}%` }}
+ className="audiobox-buttons"
+ title={this.mediaState === "paused" ? "play" : "pause"}
+ onClick={this.mediaState === "paused" ? this.Play : this.Pause}
>
- <div
- className="audiobox-buttons"
- title={this.mediaState === "paused" ? "play" : "pause"}
- onClick={this.mediaState === "paused" ? this.Play : this.Pause}
- >
- {" "}
- <FontAwesomeIcon
- icon={this.mediaState === "paused" ? "play" : "pause"}
- size={"1x"}
- />
- </div>
- <div
- className="audiobox-buttons"
- title={this._trimming ? "finish" : "trim"}
- onClick={this._trimming ? this.finishTrim : this.startTrim}
- >
- <FontAwesomeIcon
- icon={this._trimming ? "check" : "cut"}
- size={"1x"}
- />
- </div>
- <div
- className="audiobox-timeline"
- style={{
- top: 0,
- height: `100%`,
- left: AudioBox.playheadWidth,
- width: `calc(100% - ${AudioBox.playheadWidth}px)`,
- background: "white",
- }}
- >
- {this.renderTimeline}
- </div>
- {this.audio}
- <div className="audioBox-current-time">
- {this._trimming ?
- formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))
- : formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this._trimStart)))}
- </div>
- <div className="audioBox-total-time">
- {this._trimming || !this._trimEnd ?
- formatTime(Math.round(NumCast(this.duration)))
- : formatTime(Math.round(NumCast(this.trimDuration)))}
- </div>
+ {" "}
+ <FontAwesomeIcon
+ icon={this.mediaState === "paused" ? "play" : "pause"}
+ size={"1x"}
+ />
+ </div>
+ <div
+ className="audiobox-buttons"
+ title={this._trimming ? "finish" : "trim"}
+ onClick={this._trimming ? this.finishTrim : this.startTrim}
+ >
+ <FontAwesomeIcon
+ icon={this._trimming ? "check" : "cut"}
+ size={"1x"}
+ />
+ </div>
+ <div
+ className="audiobox-timeline"
+ style={{
+ top: 0,
+ height: `100%`,
+ left: AudioBox.playheadWidth,
+ width: `calc(100% - ${AudioBox.playheadWidth}px)`,
+ background: "white",
+ }}
+ >
+ {this.renderTimeline}
+ </div>
+ {this.audio}
+ <div className="audioBox-current-time">
+ {this._trimming ?
+ formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))
+ : formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this._trimStart)))}
+ </div>
+ <div className="audioBox-total-time">
+ {this._trimming || !this._trimEnd ?
+ formatTime(Math.round(NumCast(this.duration)))
+ : formatTime(Math.round(NumCast(this.trimDuration)))}
</div>
</div>
- )}
+ </div>
+ )}
</div>
);
}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index fe34d6687..c2a526804 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,6 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, Opt } from "../../../fields/Doc";
-import { Document } from "../../../fields/documentSchemas";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
import { ComputedField } from "../../../fields/ScriptField";
@@ -23,25 +22,27 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
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;
layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined;
+ renderCutoffProvider: (doc: Doc) => boolean;
zIndex?: number;
highlight?: boolean;
jitterRotation: number;
dataTransition?: string;
replica: string;
+ renderIndex: number;
CollectionFreeFormView: CollectionFreeFormView;
}
@observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, Document>(Document) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
public static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"]; // fields that are configured to be animatable using animation frames
@observable _animPos: number[] | undefined = undefined;
@observable _contentView: DocumentView | undefined | null;
get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; }
get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${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 ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
+ get X() { return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x); }
+ get Y() { return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y); }
+ get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex); }
get Opacity() { return this.dataProvider ? this.dataProvider.opacity : undefined; }
get Highlight() { return this.dataProvider?.highlight; }
@computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
@@ -176,7 +177,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
mixBlendMode,
display: this.ZInd === -99 ? "none" : undefined
}} >
- <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
+ {this.props.renderCutoffProvider(this.props.Document) ?
+ <div style={{ position: "absolute", width: this.panelWidth(), height: this.panelHeight(), background: "lightGreen" }} />
+ :
+ <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
+ }
</div>;
}
}
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 8da5cd1b1..d975baf9b 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -3,9 +3,7 @@ import { action } from "mobx";
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from 'react-color';
import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
-import { documentSchema } from "../../../fields/documentSchemas";
import { InkTool } from "../../../fields/InkField";
-import { makeInterface } from "../../../fields/Schema";
import { StrCast } from "../../../fields/Types";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -17,11 +15,8 @@ import "./ColorBox.scss";
import { FieldView, FieldViewProps } from './FieldView';
import { RichTextMenu } from "./formattedText/RichTextMenu";
-type ColorDocument = makeInterface<[typeof documentSchema]>;
-const ColorDocument = makeInterface(documentSchema);
-
@observer
-export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument>(ColorDocument) {
+export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
@undoBatch
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 750213e67..5919cd8f2 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -2,8 +2,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import { Doc, Opt } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
-import { createSchema, makeInterface } from '../../../fields/Schema';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
@@ -15,15 +13,10 @@ import "./ComparisonBox.scss";
import { DocumentView, DocumentViewProps } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import React = require("react");
-import { DocumentType } from '../../documents/DocumentTypes';
-export const comparisonSchema = createSchema({});
-
-type ComparisonDocument = makeInterface<[typeof comparisonSchema, typeof documentSchema]>;
-const ComparisonDocument = makeInterface(comparisonSchema, documentSchema);
@observer
-export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ComparisonDocument>(ComparisonDocument) {
+export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
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];
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 123212608..433a0bf48 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -2,7 +2,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import { DocumentView } from "./DocumentView";
import { DocumentManager } from "../../util/DocumentManager";
-import { Transformer, Scripting, ts } from "../../util/Scripting";
+import { Transformer, ts } from "../../util/Scripting";
import { Field } from "../../../fields/Doc";
@observer
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 228e1bdcb..9ab3171d3 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,6 +1,8 @@
@import "../global/globalCssVariables.scss";
-
+.documentLinksButton-wrapper {
+ transform-origin: top left;
+}
.documentLinksButton-menu {
width: 100%;
height: 100%;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 93cd02d93..7e6ca4248 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -20,6 +20,7 @@ import { DocumentView } from "./DocumentView";
import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
import { TaskCompletionBox } from "./TaskCompletedBox";
import React = require("react");
+import { Transform } from "../../util/Transform";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
@@ -31,6 +32,7 @@ interface DocumentLinksButtonProps {
AlwaysOn?: boolean;
InMenu?: boolean;
StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
+ ContentScaling?: () => number;
}
@observer
export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -302,16 +304,16 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
const title = this.props.InMenu ? menuTitle : buttonTitle;
//render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
- return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) :
- this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
- <Tooltip title={<div className="dash-tooltip">{title}</div>}>
- {this.linkButtonInner}
- </Tooltip>
- :
- !DocumentLinksButton.LinkEditorDocView && !this.props.InMenu ?
- <Tooltip title={<div className="dash-tooltip">{title}</div>}>
- {this.linkButtonInner}
- </Tooltip>
- : this.linkButtonInner;
+ return (!Array.from(this.filteredLinks).length && !this.props.AlwaysOn) ? (null) :
+ <div className="documentLinksButton-wrapper" >
+ {
+ (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) ||
+ (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ?
+ <Tooltip title={<div className="dash-tooltip">{title}</div>}>
+ {this.linkButtonInner}
+ </Tooltip>
+ : this.linkButtonInner
+ }
+ </div>;
}
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 9fcd45e72..4565f8504 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -58,9 +58,10 @@
.documentView-audioBackground {
display: inline-block;
width: 10%;
+ height: 25;
position: absolute;
- top: 0px;
- left: 0px;
+ top: 10px;
+ left: 10px;
border-radius: 25px;
background: white;
opacity: 0.3;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9fc4b7890..7092b335c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,3 +1,4 @@
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
@@ -9,11 +10,11 @@ import { List } from "../../../fields/List";
import { ObjectField } from "../../../fields/ObjectField";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import { AudioField } from "../../../fields/URLField";
import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick, returnEmptyString } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnVal, simulateMouseClick, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
@@ -23,7 +24,7 @@ import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
import { InteractionUtils } from '../../util/InteractionUtils';
import { LinkManager } from '../../util/LinkManager';
-import { Scripting } from '../../util/Scripting';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from "../../util/SelectionManager";
import { SharingManager } from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
@@ -48,8 +49,6 @@ import { RadialMenu } from './RadialMenu';
import { ScriptingBox } from "./ScriptingBox";
import { PresBox } from './trails/PresBox';
import React = require("react");
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { ColorScheme } from "../../util/SettingsManager";
const { Howl } = require('howler');
interface Window {
@@ -110,6 +109,7 @@ export interface DocumentViewSharedProps {
fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
+ thumbShown?: () => boolean;
setContentView?: (view: DocComponentView) => any;
CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
PanelWidth: () => number;
@@ -179,11 +179,12 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
}
@observer
-export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps, Document>(Document) {
+export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() {
public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered.
@observable _animateScalingTo = 0;
@observable _mediaState = 0;
@observable _pendingDoubleClick = false;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
private _downX: number = 0;
private _downY: number = 0;
private _firstX: number = -1;
@@ -204,6 +205,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
public get LayoutFieldKey() { return Doc.LayoutFieldKey(this.layoutDoc); }
@computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); }
@computed get ContentScale() { return this.props.ContentScaling?.() || 1; }
+ @computed get thumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); }
@computed get hidden() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Hidden); }
@computed get opacity() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity); }
@computed get boxShadow() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow); }
@@ -239,6 +241,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this._multiTouchDisposer?.();
this._holdDisposer?.();
unbrush && Doc.UnBrushDoc(this.props.Document);
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
@@ -485,6 +488,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const func = () => this.onClickHandler.script.run({
this: this.layoutDoc,
self: this.rootDoc,
+ _readOnly_: false,
scriptContext: this.props.scriptContext,
thisContainer: this.props.ContainingCollectionDoc,
documentView: this.props.DocumentView(),
@@ -835,14 +839,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this._componentView?.isAnyChildContentActive?.() ||
this.props.isContentActive()) ? true : undefined;
}
+ @observable _retryThumb = 1;
+ thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+ !this._componentView?.isAnyChildContentActive?.() ? true : false;
@computed get contents() {
TraceMobx();
const audioView = !this.layoutDoc._showAudio ? (null) :
- <div className="documentView-audioBackground"
- onPointerDown={this.recordAudioAnnotation}
- onPointerEnter={this.onPointerEnter}
- style={{ height: 25, position: "absolute", top: 10, left: 10 }}
- >
+ <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter} >
<FontAwesomeIcon className="documentView-audioFont"
style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }}
icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
@@ -852,8 +855,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
pointerEvents: this.props.pointerEvents as any ? this.props.pointerEvents as any : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
}}>
- <DocumentContentsView key={1} {...this.props}
+ {!this._retryThumb || !this.thumbShown() ? (null) :
+ <img style={{ background: "white", top: 0, position: "absolute" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
+ width={this.props.PanelWidth()} height={this.props.PanelHeight()}
+ onError={(e: any) => {
+ setTimeout(action(() => this._retryThumb = 0), 0);
+ setTimeout(action(() => this._retryThumb = 1), 150);
+ }} />}
+ <DocumentContentsView key={1}
+ {...this.props}
docViewPath={this.props.viewPath}
+ thumbShown={this.thumbShown}
setContentView={this.setContentView}
scaling={this.contentScaling}
PanelHeight={this.panelHeight}
@@ -865,11 +877,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
focus={this.focus}
layoutKey={this.finalLayoutKey} />
{this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
- {this.hideLinkButton || this.props.renderDepth === -1 ? (null) :
- <div style={{ transformOrigin: "top left", transform: `scale(${Math.min(1, this.props.ScreenToLocalTransform().scale(this.props.ContentScaling?.() || 1).Scale)})` }}>
- <DocumentLinksButton View={this.props.DocumentView()} Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
- </div>}
-
+ {this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
+ <DocumentLinksButton View={this.props.DocumentView()}
+ ContentScaling={this.props.ContentScaling}
+ Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
+ }
{audioView}
</div>;
}
@@ -983,7 +995,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ":caption");
@computed get innards() {
TraceMobx();
- const ffscale = (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1);
+ const ffscale = () => (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1);
const showTitle = this.ShowTitle?.split(":")[0];
const showTitleHover = this.ShowTitle?.includes(":hover");
const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined;
@@ -991,14 +1003,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
<div className="documentView-captionWrapper"
style={{
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined,
- minWidth: 50 * ffscale,
- maxHeight: `max(100%, ${20 * ffscale}px)`
+ minWidth: 50 * ffscale(),
+ maxHeight: `max(100%, ${20 * ffscale()}px)`
}}>
<FormattedTextBox {...OmitKeys(this.props, ['children']).omit}
yPadding={10}
xPadding={10}
fieldKey={showCaption}
- fontSize={12 * Math.max(1, 2 * ffscale / 3)}
+ fontSize={12 * Math.max(1, 2 * ffscale() / 3)}
styleProvider={this.captionStyleProvider}
dontRegisterView={true}
noSidebar={true}
@@ -1022,19 +1034,18 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
display={"block"}
fontSize={10}
GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle}
- SetValue={undoBatch(input => {
+ SetValue={undoBatch((input: string) => {
if (input?.startsWith("#")) {
if (this.props.showTitle) {
this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
} else {
Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate";
}
- return true;
} else {
- var value = input.replace(new RegExp(showTitle + "="), "");
+ var value = input.replace(new RegExp(showTitle + "="), "") as string | number;
if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
if (showTitle.includes("Date") || showTitle === "author") return true;
- return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
+ Doc.SetInPlace(targetDoc, showTitle, value, true);
}
return true;
})}
@@ -1047,15 +1058,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{captionView}
</div>;
}
+ @observable _: string = "";
@computed get renderDoc() {
TraceMobx();
- const isButton: boolean = this.props.Document.type === DocumentType.FONTICON;
+ const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
+ const isButton = this.props.Document.type === DocumentType.FONTICON;
if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
return this.docContents ??
<div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
id={this.props.Document[Id]}
style={{
- background: isButton ? undefined : this.backgroundColor,
+ background: isButton || thumb ? undefined : this.backgroundColor,
opacity: this.opacity,
color: StrCast(this.layoutDoc.color, "inherit"),
fontFamily: StrCast(this.Document._fontFamily, "inherit"),
@@ -1064,6 +1077,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
}}>
+
{this.innards}
{this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : (null)}
{this.widgetDecorations ?? null}
@@ -1142,7 +1156,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; }
get fitWidth() { return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth; }
- @computed get docViewPath() { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
+ @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
@computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
@computed get nativeWidth() {
return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
@@ -1280,7 +1294,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
}
-Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
+ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
else dv.switchViews(true, detailLayoutKeySuffix);
}); \ No newline at end of file
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index f1f802c13..c170f9867 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -3,25 +3,18 @@ import { action, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { WidthSym } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
-import { createSchema, makeInterface } from '../../../fields/Schema';
import { NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { Docs } from '../../documents/Documents';
import { ViewBoxBaseComponent } from '../DocComponent';
import { LightboxView } from '../LightboxView';
-import { FieldView, FieldViewProps } from './FieldView';
import './EquationBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
-const EquationSchema = createSchema({});
-
-type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>;
-const EquationDocument = makeInterface(EquationSchema, documentSchema);
-
@observer
-export class EquationBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) {
+export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); }
public static SelectOnLoad: string = "";
_ref: React.RefObject<EquationEditor> = React.createRef();
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index fb8e89da9..ba65acee0 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -4,18 +4,17 @@ import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import Select from "react-select";
import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
import { List } from "../../../fields/List";
import { RichTextField } from "../../../fields/RichTextField";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, StrCast, NumCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { UserOptions } from "../../util/GroupManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
import { SelectionManager } from "../../util/SelectionManager";
import { CollectionTreeView } from "../collections/CollectionTreeView";
import { CollectionView } from "../collections/CollectionView";
@@ -30,11 +29,8 @@ const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-type FilterBoxDocument = makeInterface<[typeof documentSchema]>;
-const FilterBoxDocument = makeInterface(documentSchema);
-
@observer
-export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) {
+export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
constructor(props: Readonly<FieldViewProps>) {
super(props);
@@ -425,7 +421,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
treeViewHideTitle={true}
focus={returnFalse}
treeViewHideHeaderFields={false}
- onCheckedClick={this.scriptField}
dontRegisterView={true}
styleProvider={this.FilterStyleProvider}
layerProvider={this.props.layerProvider}
@@ -475,7 +470,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
}
}
-Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
+ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
for (const filter of docFilters) {
const fields = filter.split(":"); // split into key:value:modifiers
@@ -485,7 +480,7 @@ Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader:
}
return undefined;
});
-Scripting.addGlobal(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
+ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
const allCollectionDocs = new Set<Doc>();
const activeTabs = DocListCast(layoutDoc[childKey]);
SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 5050fc2d2..3ab0a3ff2 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -19,7 +19,7 @@ type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSch
const EquationDocument = makeInterface(EquationSchema, documentSchema);
@observer
-export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) {
+export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FunctionPlotBox, fieldKey); }
public static GraphCount = 0;
_plot: any;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 89f70985c..0a4168698 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,12 +1,12 @@
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from "mobx-react";
+import { extname } from 'path';
import { DataSym, Doc, DocListCast, 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 { ObjectField } from '../../../fields/ObjectField';
-import { createSchema, makeInterface } from '../../../fields/Schema';
+import { createSchema } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
@@ -29,16 +29,11 @@ import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
-import { SnappingManager } from '../../util/SnappingManager';
-const path = require('path');
export const pageSchema = createSchema({
googlePhotosUrl: "string",
googlePhotosTags: "string"
});
-type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
-const ImageDocument = makeInterface(pageSchema, documentSchema);
-
const uploadIcons = {
idle: "downarrow.png",
loading: "loading.gif",
@@ -47,7 +42,7 @@ const uploadIcons = {
};
@observer
-export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ImageDocument>(ImageDocument) {
+export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
@@ -190,7 +185,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href; //Why is this here
- const ext = path.extname(url.href);
+ const ext = extname(url.href);
return url.href.replace(ext, this._curSuffix + ext);
}
@@ -286,7 +281,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
- <div className="imageBox-fader" style={{ overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined }} >
+ <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? "auto" : undefined }} >
<img key="paths" ref={this._imgRef}
src={srcpath}
style={{ transform, transformOrigin }}
@@ -376,7 +371,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
scrollTop={0}
down={this._marqueeing}
scaling={this.props.scaling}
- docView={this.props.docViewPath().lastElement()}
+ docView={this.props.docViewPath().slice(-1)[0]}
addDocument={this.addDocument}
finishMarquee={this.finishMarquee}
savedAnnotations={this._savedAnnotations}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 111509fdb..c44c8c828 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -58,7 +58,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
value = eq ? value.substr(1) : value;
const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false;
value = dubEq ? value.substr(2) : value;
- const options: ScriptOptions = { addReturn: true, params: { this: "Doc", _last_: "any" }, editable: false };
+ const options: ScriptOptions = { addReturn: true, params: { this: Doc.name, self: Doc.name, _last_: "any", _readOnly_: "boolean" }, editable: false };
if (dubEq) options.typecheck = false;
const script = CompileScript(value, options);
return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq };
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 90b9ce55d..4e0461650 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -2,9 +2,8 @@ import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
import { List } from '../../../fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { listSpec } from '../../../fields/Schema';
import { Cast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
@@ -16,17 +15,13 @@ import { FieldView, FieldViewProps } from './FieldView';
import BigText from './LabelBigText';
import './LabelBox.scss';
-const LabelSchema = createSchema({});
-
-type LabelDocument = makeInterface<[typeof LabelSchema, typeof documentSchema]>;
-const LabelDocument = makeInterface(LabelSchema, documentSchema);
export interface LabelBoxProps {
label?: string;
}
@observer
-export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps), LabelDocument>(LabelDocument) {
+export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps)>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); }
public static LayoutStringWithTitle(fieldType: { name: string }, fieldStr: string, label: string) {
return `<${fieldType.name} fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index a7bfb93eb..437d29f39 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -2,8 +2,6 @@ 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 { makeInterface } from "../../../fields/Schema";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -23,11 +21,9 @@ const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-type LinkAnchorSchema = makeInterface<[typeof documentSchema]>;
-const LinkAnchorDocument = makeInterface(documentSchema);
@observer
-export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); }
_doubleTap = false;
_lastTap: number = 0;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 879a63248..43f4b43fb 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,7 +1,5 @@
import React = require("react");
import { observer } from "mobx-react";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { makeInterface } from "../../../fields/Schema";
import { emptyFunction, returnFalse } from "../../../Utils";
import { ViewBoxBaseComponent } from "../DocComponent";
import { StyleProp } from "../StyleProvider";
@@ -9,11 +7,8 @@ import { ComparisonBox } from "./ComparisonBox";
import { FieldView, FieldViewProps } from './FieldView';
import "./LinkBox.scss";
-type LinkDocument = makeInterface<[typeof documentSchema]>;
-const LinkDocument = makeInterface(documentSchema);
-
@observer
-export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(LinkDocument) {
+export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
isContentActiveFunc = () => this.isContentActive();
render() {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 50444c73a..aa2130af5 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,14 +1,11 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import * as dotenv from 'dotenv';
import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
import { observer } from "mobx-react";
import * as React from "react";
import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
-import { makeInterface } from '../../../../fields/Schema';
import { NumCast, StrCast } from '../../../../fields/Types';
import { TraceMobx } from '../../../../fields/util';
import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
@@ -43,9 +40,6 @@ import { MapBoxInfoWindow } from './MapBoxInfoWindow';
// const _global = (window /* browser */ || global /* node */) as any;
-type MapDocument = makeInterface<[typeof documentSchema]>;
-const MapDocument = makeInterface(documentSchema);
-
const mapContainerStyle = {
height: '100%',
};
@@ -59,7 +53,6 @@ const mapOptions = {
fullscreenControl: false,
};
-dotenv.config({ path: __dirname + '/.env' });
const apiKey = process.env.GOOGLE_MAPS;
const script = document.createElement('script');
@@ -93,7 +86,7 @@ const options = {
} as google.maps.places.AutocompleteOptions;
@observer
-export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>, MapDocument>(MapDocument) {
+export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index f44355929..8a68f9647 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -248,6 +248,7 @@
.pdfBox-background {
width: 100%;
height: 100%;
+ cursor: ew-resize;
background: lightGray;
}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d54b65d92..9807cee7c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,17 +3,14 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCast, Opt, WidthSym, StrListCast } from "../../../fields/Doc";
-import { documentSchema } from '../../../fields/documentSchemas';
-import { makeInterface, listSpec } from "../../../fields/Schema";
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc";
+import { Cast, NumCast, StrCast, ImageCast } from '../../../fields/Types';
import { PdfField } from "../../../fields/URLField";
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { KeyCodes } from '../../util/KeyCodes';
import { undoBatch } from '../../util/UndoManager';
-import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
@@ -22,18 +19,14 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
import { PDFViewer } from "../pdf/PDFViewer";
import { SidebarAnnos } from '../SidebarAnnos';
import { FieldView, FieldViewProps } from './FieldView';
-import { pageSchema } from "./ImageBox";
import "./PDFBox.scss";
import React = require("react");
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-
-type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
-const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@observer
-export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, PdfDocument>(PdfDocument) {
+export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); }
public static openSidebarWidth = 250;
+ public static sidebarResizerWidth = 5;
private _searchString: string = "";
private _initialScrollTarget: Opt<Doc>;
private _pdfViewer: PDFViewer | undefined;
@@ -46,6 +39,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private _pageControls = false;
@computed get pdfUrl() { return Cast(this.dataDoc[this.props.fieldKey], PdfField); }
+ @computed get pdfThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; }
constructor(props: any) {
super(props);
@@ -54,7 +48,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
!this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
if (this.pdfUrl) {
if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href));
- else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf));
+ else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => this._pdf = pdf));
}
}
@@ -80,7 +74,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const anchor =
AnchorMenu.Instance?.GetAnchor() ??
Docs.Create.TextanchorDocument({
- title: StrCast(this.rootDoc.title + "@" + this.layoutDoc._scrollTop?.toFixed(0)),
+ title: StrCast(this.rootDoc.title + "@" + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
y: NumCast(this.layoutDoc._scrollTop),
unrendered: true
});
@@ -110,8 +104,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
public prevAnnotation = () => this._pdfViewer?.prevAnnotation();
public nextAnnotation = () => this._pdfViewer?.nextAnnotation();
- public backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; };
- public forwardPage = () => { this.Document._curPage = (this.Document._curPage || 1) + 1; return true; };
+ public backPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) - 1; return true; };
+ public forwardPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) + 1; return true; };
public gotoPage = (p: number) => this.Document._curPage = p;
@undoBatch
@@ -142,15 +136,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!this.layoutDoc._showSidebar) this.toggleSidebar();
return this.addDocument(doc, sidebarKey);
}
- sidebarBtnDown = (e: React.PointerEvent) => {
+ sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
setupMoveUpEvents(this, e, (e, down, delta) => {
const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
- const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+ const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
- this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
+ onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
return false;
@@ -160,16 +154,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable _previewWidth: Opt<number> = undefined;
toggleSidebar = action((preview: boolean = false) => {
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
- const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+ const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+ const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
if (preview) {
- this._previewNativeWidth = nativeWidth * ratio;
- this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+ this._previewNativeWidth = nativeWidth * sideratio;
+ this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
this._showSidebar = true;
}
else {
- this.layoutDoc.nativeWidth = nativeWidth * ratio;
- this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+ this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
+ this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
});
@@ -185,7 +180,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</button>
</>;
const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
- const curPage = this.Document._curPage || 1;
+ const curPage = NumCast(this.Document._curPage) || 1;
return !this.props.isContentActive() ? (null) :
<div className="pdfBox-ui" onKeyDown={e => [KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true}
onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
@@ -223,7 +218,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
}}
- onPointerDown={this.sidebarBtnDown} >
+ onPointerDown={e => this.sidebarBtnDown(e, true)} >
<FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
</div>
</div>;
@@ -253,18 +248,20 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
contentScaling = () => 1;
+ isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected();
@computed get renderPdfView() {
TraceMobx();
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const scale = previewScale * (this.props.scaling?.() || 1);
return <div className={"pdfBox"} onContextMenu={this.specificContextMenu}
style={{
+ display: this.props.thumbShown?.() ? "none" : undefined,
height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ?
NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined
}}>
- <div className="pdfBox-background" />
+ <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
<div style={{
- width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
+ width: `calc(${100 / scale}% - ${(this.sidebarWidth() + PDFBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
height: `${100 / scale}%`,
transform: `scale(${scale})`,
position: "absolute",
@@ -277,7 +274,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
dataDoc={this.dataDoc}
pdf={this._pdf!}
url={this.pdfUrl!.url.pathname}
- isContentActive={this.props.isContentActive}
+ isContentActive={this.isPdfContentActive}
anchorMenuClick={this.anchorMenuClick}
loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
setPdfViewer={this.setPdfViewer}
@@ -307,17 +304,22 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>();
- static pdfpromise = new Map<string, Pdfjs.PDFPromise<Pdfjs.PDFDocumentProxy>>();
+ static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>();
render() {
TraceMobx();
- if (this._pdf) return this.renderPdfView;
+ if (this._pdf) {
+ if (!this.props.thumbShown?.()) {
+ return this.renderPdfView;
+ }
+ return null;
+ }
const href = this.pdfUrl?.url.href;
if (href) {
if (PDFBox.pdfcache.get(href)) setTimeout(action(() => this._pdf = PDFBox.pdfcache.get(href)));
else {
if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise);
- PDFBox.pdfpromise.get(href)?.then(action(pdf => PDFBox.pdfcache.set(href, this._pdf = pdf)));
+ PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, this._pdf = pdf)));
}
}
return this.renderTitleBox;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 0c631e5f9..dbb567d3a 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,20 +1,17 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
// import { Canvas } from '@react-three/fiber';
-import { action, computed, observable, reaction, trace, runInAction } from "mobx";
+import { computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
// import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
import { DateField } from "../../../fields/DateField";
-import { Doc, WidthSym, HeightSym } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
+import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
-import { InkTool } from "../../../fields/InkField";
-import { makeInterface } from "../../../fields/Schema";
import { ComputedField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
import { AudioField, VideoField } from "../../../fields/URLField";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, numberRange, OmitKeys, returnFalse, returnOne, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnFalse, returnOne } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { Networking } from "../../Network";
@@ -32,9 +29,6 @@ declare class MediaRecorder {
constructor(e: any, options?: any); // whatever MediaRecorder has
}
-type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
-const ScreenshotDocument = makeInterface(documentSchema);
-
// interface VideoTileProps {
// raised: { coord: Vector2, off: Vector3 }[];
// setRaised: (r: { coord: Vector2, off: Vector3 }[]) => void;
@@ -112,7 +106,7 @@ const ScreenshotDocument = makeInterface(documentSchema);
// }
@observer
-export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
+export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
private _audioRec: any;
private _videoRec: any;
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 183140cd7..366c3fc2f 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,18 +1,19 @@
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
import "@webscopeio/react-textarea-autocomplete/style.css";
-import { action, computed, observable, runInAction, trace } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import { Doc } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
import { List } from "../../../fields/List";
-import { createSchema, listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../fields/Types";
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
import { returnEmptyString } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
import { InteractionUtils } from "../../util/InteractionUtils";
-import { CompileScript, Scripting, ScriptParam } from "../../util/Scripting";
+import { CompileScript, ScriptParam } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
import { ScriptManager } from "../../util/ScriptManager";
import { ContextMenu } from "../ContextMenu";
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
@@ -21,15 +22,10 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
import { OverlayView } from "../OverlayView";
import { DocumentIconContainer } from "./DocumentIcon";
import "./ScriptingBox.scss";
-import { TraceMobx } from "../../../fields/util";
const _global = (window /* browser */ || global /* node */) as any;
-const ScriptingSchema = createSchema({});
-type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>;
-const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema);
-
@observer
-export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScriptingDocument>(ScriptingDocument) {
+export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
@@ -42,9 +38,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
@observable private _function: boolean = false;
@observable private _spaced: boolean = false;
- @observable private _scriptKeys: any = Scripting.getGlobals();
- @observable private _scriptingDescriptions: any = Scripting.getDescriptions();
- @observable private _scriptingParams: any = Scripting.getParameters();
+ @observable private _scriptKeys: any = ScriptingGlobals.getGlobals();
+ @observable private _scriptingDescriptions: any = ScriptingGlobals.getDescriptions();
+ @observable private _scriptingParams: any = ScriptingGlobals.getParameters();
@observable private _currWord: string = "";
@observable private _suggestions: string[] = [];
@@ -224,9 +220,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
ScriptManager.Instance.addScript(this.dataDoc);
- this._scriptKeys = Scripting.getGlobals();
- this._scriptingDescriptions = Scripting.getDescriptions();
- this._scriptingParams = Scripting.getParameters();
+ this._scriptKeys = ScriptingGlobals.getGlobals();
+ this._scriptingDescriptions = ScriptingGlobals.getDescriptions();
+ this._scriptingParams = ScriptingGlobals.getParameters();
}
// overlays document numbers (ex. d32) over all documents when clicked on
@@ -577,8 +573,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
</div>;
}
- renderFuncListElement(value: string) {
- return <div>
+ renderFuncListElement(value: string | object) {
+ return (typeof value !== "string") ? (null) : <div>
<div style={{ fontSize: "14px" }}>
{value}
</div>
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 92d1f7446..b96977f32 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -2,30 +2,19 @@ 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 '../../../fields/documentSchemas';
-import { createSchema, makeInterface } from '../../../fields/Schema';
import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxBaseComponent } from '../DocComponent';
import { ScriptBox } from '../ScriptBox';
+import { StyleProp } from '../StyleProvider';
import { FieldView, FieldViewProps } from './FieldView';
import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
import './SliderBox.scss';
-import { StyleProp } from '../StyleProvider';
-
-const SliderSchema = createSchema({
- _sliderMin: "number",
- _sliderMax: "number",
- _sliderMinThumb: "number",
- _sliderMaxThumb: "number",
-});
-type SliderDocument = makeInterface<[typeof SliderSchema, typeof documentSchema]>;
-const SliderDocument = makeInterface(SliderSchema, documentSchema);
@observer
-export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocument>(SliderDocument) {
+export class SliderBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); }
get minThumbKey() { return this.fieldKey + "-minThumb"; }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 615d595c0..33fdc4935 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,18 +1,20 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from "mobx";
import { observer } from "mobx-react";
+import { basename } from "path";
import * as rp from 'request-promise';
import { Doc, DocListCast } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
import { InkTool } from "../../../fields/InkField";
-import { makeInterface } from "../../../fields/Schema";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { AudioField, nullAudio, VideoField } from "../../../fields/URLField";
-import { emptyFunction, formatTime, OmitKeys, returnOne, setupMoveUpEvents, Utils, returnFalse } from "../../../Utils";
+import { AudioField, VideoField } from "../../../fields/URLField";
+import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
import { Networking } from "../../Network";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { DocumentManager } from "../../util/DocumentManager";
import { SelectionManager } from "../../util/SelectionManager";
import { SnappingManager } from "../../util/SnappingManager";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -22,22 +24,14 @@ import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
import { MarqueeAnnotator } from "../MarqueeAnnotator";
+import { AnchorMenu } from "../pdf/AnchorMenu";
import { StyleProp } from "../StyleProvider";
import { FieldView, FieldViewProps } from './FieldView';
import { LinkDocPreview } from "./LinkDocPreview";
import "./VideoBox.scss";
-import { DragManager } from "../../util/DragManager";
-import { DocumentManager } from "../../util/DocumentManager";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { Tooltip } from "@material-ui/core";
-import { AnchorMenu } from "../pdf/AnchorMenu";
-const path = require('path');
-
-type VideoDocument = makeInterface<[typeof documentSchema]>;
-const VideoDocument = makeInterface(documentSchema);
@observer
-export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, VideoDocument>(VideoDocument) {
+export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
static _youtubeIframeCounter: number = 0;
static Instance: VideoBox;
@@ -83,7 +77,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const aspect = this.player!.videoWidth / this.player!.videoHeight;
Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
- this.layoutDoc._height = (this.layoutDoc._width || 0) / aspect;
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
if (Number.isFinite(this.player!.duration)) {
this.dataDoc[this.fieldKey + "-duration"] = this.player!.duration;
}
@@ -126,7 +120,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
this._playTimer = undefined;
- this.updateTimecode();
+ this.props.renderDepth !== -1 && this.updateTimecode();
}
@action public FullScreen = () => {
@@ -140,7 +134,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
@action public Snapshot(downX?: number, downY?: number) {
- const width = (this.layoutDoc._width || 0);
+ const width = NumCast(this.layoutDoc._width);
const canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
@@ -154,7 +148,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (!this._videoRef) {
const b = Docs.Create.LabelDocument({
- x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1),
+ x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
_width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
_isLinkButton: true
});
@@ -175,7 +169,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
// if you want to preview the captured image,
const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, "");
- const filename = path.basename(encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
+ const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
+ const filename = basename(encodedFilename);
VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY));
}
@@ -183,12 +178,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
- const width = this.layoutDoc._width || 1;
- const height = this.layoutDoc._height || 0;
+ const width = NumCast(this.layoutDoc._width) || 1;
+ const height = NumCast(this.layoutDoc._height);
const imageSummary = Docs.Create.ImageDocument(url, {
_nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
- x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), _isLinkButton: true,
- _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-"
+ x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
+ _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
});
Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
@@ -234,7 +229,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (!nativeWidth || !nativeHeight) {
if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
- this.layoutDoc._height = (this.layoutDoc._width || 0) / youtubeaspect;
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
}
}
}
@@ -252,18 +247,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// @ts-ignore
vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
this._disposers.reactionDisposer?.();
- this._disposers.reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
+ this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
}
}
- public static async convertDataUri(imageUri: string, returnedFilename: string) {
+ public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false) {
try {
const posting = Utils.prepend("/uploadURI");
const returnedUri = await rp.post(posting, {
body: {
uri: imageUri,
- name: returnedFilename
+ name: returnedFilename,
+ nosuffix
},
json: true,
});
@@ -359,7 +355,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const onYoutubePlayerReady = (event: any) => {
this._disposers.reactionDisposer?.();
this._disposers.youtubeReactionDisposer?.();
- this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0)));
+ this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
this._disposers.youtubeReactionDisposer = reaction(
() => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
(interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
@@ -377,7 +373,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
}
private get uIButtons() {
- const curTime = (this.layoutDoc._currentTimecode || 0);
+ const curTime = NumCast(this.layoutDoc._currentTimecode);
const nonNativeControls = [
<Tooltip title={<div className="dash-tooltip">{"playback"}</div>} key="play" placement="bottom">
<div className="videoBox-play" onPointerDown={this.onPlayDown} >
@@ -445,7 +441,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
onResetDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e,
(e: PointerEvent) => {
- this.Seek(Math.max(0, (this.layoutDoc._currentTimecode || 0) + Math.sign(e.movementX) * 0.0333));
+ this.Seek(Math.max(0, NumCast(this.layoutDoc._currentTimecode) + Math.sign(e.movementX) * 0.0333));
e.stopImmediatePropagation();
return false;
},
@@ -457,7 +453,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
- const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
+ const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
onPointerLeave={this.updateTimecode}
onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
@@ -542,8 +538,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
trimStart={0}
trimEnd={this.duration}
trimDuration={this.duration}
- setStartTrim={() => { }}
- setEndTrim={() => { }}
+ setStartTrim={emptyFunction}
+ setEndTrim={emptyFunction}
/>
</div>;
}
@@ -567,7 +563,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.props.select(true);
});
- @computed get fitWidth() { return this.props.docViewPath?.().lastElement().fitWidth; }
+ @computed get fitWidth() { return this.props.docViewPath?.().slice(-1)[0].fitWidth; }
contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
scaling = () => this.props.scaling?.() || 1;
panelWidth = (): number => this.fitWidth ? this.props.PanelWidth() : (Doc.NativeAspect(this.rootDoc) || 1) * this.panelHeight();
@@ -586,7 +582,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
style={{
pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
borderRadius,
- overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined
+ overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
}} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
<div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
<div style={{
@@ -622,7 +618,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
scrollTop={0}
down={this._marqueeing}
scaling={this.marqueeFitScaling}
- docView={this.props.docViewPath().lastElement()}
+ docView={this.props.docViewPath().slice(-1)[0]}
containerOffset={this.marqueeOffset}
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 79289abaa..7dc970496 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -5,6 +5,12 @@
height: 100%;
position: relative;
display: flex;
+ .webBox-background {
+ width: 100%;
+ height: 100%;
+ cursor: ew-resize;
+ background: lightGray;
+ }
.webBox-ui {
position: absolute;
@@ -137,6 +143,7 @@
transform-origin: top left;
width: 100%;
height: 100%;
+ position: absolute;
.webBox-htmlSpan {
position: absolute;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 9956cc36b..7ff47107e 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -3,21 +3,20 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from "mobx-react";
import * as WebRequest from 'web-request';
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { HtmlField } from "../../../fields/HtmlField";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { WebField } from "../../../fields/URLField";
+import { Cast, ImageCast, NumCast, StrCast } from "../../../fields/Types";
+import { ImageField, WebField } from "../../../fields/URLField";
import { TraceMobx } from "../../../fields/util";
import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { KeyCodes } from "../../util/KeyCodes";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
import { SnappingManager } from "../../util/SnappingManager";
import { undoBatch } from "../../util/UndoManager";
import { MarqueeOptionsMenu } from "../collections/collectionFreeForm";
@@ -36,18 +35,18 @@ import { StyleProp } from "../StyleProvider";
import { DocumentViewProps } from "./DocumentView";
import { FieldView, FieldViewProps } from './FieldView';
import { LinkDocPreview } from "./LinkDocPreview";
+import { VideoBox } from "./VideoBox";
import "./WebBox.scss";
import React = require("react");
+const { CreateImage } = require("./WebBoxRenderer");
const _global = (window /* browser */ || global /* node */) as any;
const htmlToText = require("html-to-text");
-type WebDocument = makeInterface<[typeof documentSchema]>;
-const WebDocument = makeInterface(documentSchema);
-
@observer
-export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, WebDocument>(WebDocument) {
+export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
public static openSidebarWidth = 250;
+ public static sidebarResizerWidth = 5;
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -63,6 +62,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private _searching: boolean = false;
@observable private _showSidebar = false;
@observable private _scrollTimer: any;
+ @observable private _webPageHasBeenRendered = false;
@observable private _overlayAnnoInfo: Opt<Doc>;
@observable private _marqueeing: number[] | undefined;
@observable private _isAnnotating = false;
@@ -76,6 +76,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
@computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
@computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
+ @computed get webThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; }
constructor(props: any) {
super(props);
@@ -103,7 +104,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return true;
}
async componentDidMount() {
- this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+ this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document. this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
runInAction(() => {
this._annotationKeySuffix = () => this._urlHash + "-annotations";
@@ -111,6 +112,48 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`);
this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`);
});
+ reaction(() => this.props.isSelected(),
+ async (selected) => {
+ if (selected) {
+ this._webPageHasBeenRendered = true;
+ setTimeout(action(() => {
+ this._scrollHeight = Math.max(this.scrollHeight, this._iframe?.contentDocument?.body.scrollHeight || 0);
+ if (this._initialScroll !== undefined && this._outerRef.current) {
+ setTimeout(() => {
+ this._outerRef.current!.scrollTop = this._initialScroll!;
+ this._initialScroll = undefined;
+ });
+ }
+ }));
+ } else if (!this.props.isContentActive() &&
+ !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && /// don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
+ LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
+ const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
+ if (this._iframe && !imageBitmap) {
+ var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
+ if (!htmlString) {
+ htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
+ }
+ this.layoutDoc.thumb = undefined;
+ const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
+ CreateImage(
+ this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
+ this._iframe.contentDocument?.styleSheets ?? [],
+ htmlString,
+ nativeWidth,
+ nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+ NumCast(this.layoutDoc._scrollTop)
+ ).then
+ ((dataUrl: any) => {
+ VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then(
+ returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500));
+ })
+ .catch(function (error: any) {
+ console.error('oops, something went wrong!', error);
+ });
+ }
+ }
+ });
this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight,
autoHeight => {
@@ -292,12 +335,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
iframe.contentDocument.addEventListener("pointerdown", this.iframeDown);
this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight);
setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
- const initialScroll = this._initialScroll;
- if (initialScroll !== undefined && this._outerRef.current) {
- // bcz: not sure why this happens, but if the webpage isn't ready yet, it's scroll height seems to be limited. So we need to wait tp set scroll location to what we want.
- setTimeout(() => this._outerRef.current!.scrollTop = initialScroll);
- this._initialScroll = undefined;
- }
iframe.setAttribute("enable-annotation", "true");
iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => {
let href = "";
@@ -353,42 +390,46 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
- @action
- forward = () => {
+ forward = (checkAvailable?: boolean) => {
const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []);
const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
- if (future.length) {
- const curUrl = this._url;
- this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
- this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
- if (this._webUrl === this._url) {
- this._webUrl = curUrl;
- setTimeout(action(() => this._webUrl = this._url));
- } else {
- this._webUrl = this._url;
+ if (checkAvailable) return future.length;
+ runInAction(() => {
+ if (future.length) {
+ const curUrl = this._url;
+ this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
+ this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
+ if (this._webUrl === this._url) {
+ this._webUrl = curUrl;
+ setTimeout(action(() => this._webUrl = this._url));
+ } else {
+ this._webUrl = this._url;
+ }
+ return true;
}
- return true;
- }
+ });
return false;
}
- @action
- back = () => {
+ back = (checkAvailable?: boolean) => {
const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
- if (history.length) {
- const curUrl = this._url;
- if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
- else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
- this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
- if (this._webUrl === this._url) {
- this._webUrl = curUrl;
- setTimeout(action(() => this._webUrl = this._url));
- } else {
- this._webUrl = this._url;
+ if (checkAvailable) return history.length;
+ runInAction(() => {
+ if (history.length) {
+ const curUrl = this._url;
+ if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
+ else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
+ this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
+ if (this._webUrl === this._url) {
+ this._webUrl = curUrl;
+ setTimeout(action(() => this._webUrl = this._url));
+ } else {
+ this._webUrl = this._url;
+ }
+ return true;
}
- return true;
- }
+ });
return false;
}
@@ -452,8 +493,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
<button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}>
GO
</button>
- <button className="submitUrl" onClick={this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button>
- <button className="submitUrl" onClick={this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" /> </button>
+ <button className="submitUrl" onClick={() => this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button>
+ <button className="submitUrl" onClick={() => this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" /> </button>
</div>
</div>
);
@@ -512,7 +553,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
@computed get urlContent() {
- if (this._hackHide) return (null);
+ if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null);
const field = this.dataDoc[this.props.fieldKey];
let view;
if (field instanceof HtmlField) {
@@ -530,6 +571,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
}
+ setTimeout(action(() => this._webPageHasBeenRendered = true));
return view;
}
@@ -542,34 +584,37 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!this.layoutDoc._showSidebar) this.toggleSidebar();
return this.addDocumentWrapper(doc, sidebarKey);
}
- sidebarBtnDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, (e, down, delta) => {
+ @observable _draggingSidebar = false;
+ sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
+ setupMoveUpEvents(this, e, action((e, down, delta) => {
+ this._draggingSidebar = true;
const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
- const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+ const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
- this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
+ onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
return false;
- }, emptyFunction, () => this.toggleSidebar());
+ }), action(() => this._draggingSidebar = false), () => this.toggleSidebar());
}
@observable _previewNativeWidth: Opt<number> = undefined;
@observable _previewWidth: Opt<number> = undefined;
toggleSidebar = action((preview: boolean = false) => {
const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
- const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+ const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+ const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
if (preview) {
- this._previewNativeWidth = nativeWidth * ratio;
- this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+ this._previewNativeWidth = nativeWidth * sideratio;
+ this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
this._showSidebar = true;
}
else {
- this.layoutDoc.nativeWidth = nativeWidth * ratio;
- this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+ this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
+ this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
});
@@ -633,7 +678,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
return this.props.styleProvider?.(doc, props, property);
}
- pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
+ pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
render() {
const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents ? this.props.pointerEvents as any : undefined;
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
@@ -664,13 +709,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
return (
<div className="webBox" ref={this._mainCont}
- style={{ pointerEvents: this.pointerEvents() }} >
- <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}>
+ style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? "none" : undefined }} >
+ <div className="webBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
+ <div className="webBox-container" style={{
+ position: "absolute",
+ width: `calc(${100 / scale}% - ${(this.sidebarWidth() + WebBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
+ transform: `scale(${scale})`,
+ pointerEvents
+ }} onContextMenu={this.specificContextMenu}>
<div className={"webBox-outerContent"} ref={this._outerRef}
style={{
- width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
height: `${100 / scale}%`,
- transform: `scale(${scale})`,
pointerEvents
}}
onWheel={e => { e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe
@@ -703,6 +752,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</div >
<SidebarAnnos ref={this._sidebarRef}
{...this.props}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
fieldKey={this.fieldKey + "-" + this._urlHash}
rootDoc={this.rootDoc}
layoutDoc={this.layoutDoc}
@@ -720,11 +770,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
}}
- onPointerDown={this.sidebarBtnDown} >
+ onPointerDown={e => this.sidebarBtnDown(e, true)} >
<FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
</div>
{!this.props.isContentActive() ? (null) : this.searchUI}
</div>);
}
}
-Scripting.addGlobal(function urlHash(url: string) { return WebBox.urlHash(url); }); \ No newline at end of file
+ScriptingGlobals.add(function urlHash(url: string) { return WebBox.urlHash(url); }); \ No newline at end of file
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
new file mode 100644
index 000000000..08a5746d1
--- /dev/null
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -0,0 +1,395 @@
+/**
+ *
+ * @param {StyleSheetList} styleSheets
+ */
+var ForeignHtmlRenderer = function (styleSheets) {
+
+ const self = this;
+
+ /**
+ *
+ * @param {String} binStr
+ */
+ const binaryStringToBase64 = function (binStr) {
+ return new Promise(function (resolve) {
+ const reader = new FileReader();
+ reader.readAsDataURL(binStr);
+ reader.onloadend = function () {
+ resolve(reader.result);
+ }
+ });
+ };
+
+ function prepend(extension) {
+ return window.location.origin + extension;
+ }
+ function CorsProxy(url) {
+ return prepend("/corsProxy/") + encodeURIComponent(url);
+ }
+ /**
+ *
+ * @param {String} url
+ * @returns {Promise}
+ */
+ const getResourceAsBase64 = function (webUrl, inurl) {
+ return new Promise(function (resolve, reject) {
+ const xhr = new XMLHttpRequest();
+ //const url = inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl;
+ //const url = CorsProxy(inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl);// inurl.startsWith("http") ? CorsProxy(inurl) : inurl;
+ var url = inurl;
+ if (inurl.startsWith("/static")) {
+ url = (new URL(webUrl).origin + inurl);
+ } else
+ if ((inurl.startsWith("/") && !inurl.startsWith("//"))) {
+ url = CorsProxy(new URL(webUrl).origin + inurl);
+ } else if (!inurl.startsWith("http") && !inurl.startsWith("//")) {
+ url = CorsProxy(webUrl + "/" + inurl);
+ }
+ xhr.open("GET", url);
+ xhr.responseType = 'blob';
+
+ xhr.onreadystatechange = async function () {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ const resBase64 = await binaryStringToBase64(xhr.response);
+
+ resolve(
+ {
+ "resourceUrl": inurl,
+ "resourceBase64": resBase64
+ }
+ );
+ } else if (xhr.readyState === 4) {
+ console.log("COULDN'T FIND: " + (inurl.startsWith("/") ? webUrl + inurl : inurl));
+ resolve(
+ {
+ "resourceUrl": "",
+ "resourceBase64": inurl
+ }
+ );
+ }
+ };
+
+ xhr.send(null);
+ });
+ };
+
+ /**
+ *
+ * @param {String[]} urls
+ * @returns {Promise}
+ */
+ const getMultipleResourcesAsBase64 = function (webUrl, urls) {
+ const promises = [];
+ for (let i = 0; i < urls.length; i++) {
+ promises.push(getResourceAsBase64(webUrl, urls[i]));
+ }
+ return Promise.all(promises);
+ };
+
+ /**
+ *
+ * @param {String} str
+ * @param {Number} startIndex
+ * @param {String} prefixToken
+ * @param {String[]} suffixTokens
+ *
+ * @returns {String|null}
+ */
+ const parseValue = function (str, startIndex, prefixToken, suffixTokens) {
+ const idx = str.indexOf(prefixToken, startIndex);
+ if (idx === -1) {
+ return null;
+ }
+
+ let val = '';
+ for (let i = idx + prefixToken.length; i < str.length; i++) {
+ if (suffixTokens.indexOf(str[i]) !== -1) {
+ break;
+ }
+
+ val += str[i];
+ }
+
+ return {
+ "foundAtIndex": idx,
+ "value": val
+ }
+ };
+
+ /**
+ *
+ * @param {String} cssRuleStr
+ * @returns {String[]}
+ */
+ const getUrlsFromCssString = function (cssRuleStr, selector = "url(", delimiters = [')'], mustEndWithQuote = false) {
+ const urlsFound = [];
+ let searchStartIndex = 0;
+
+ while (true) {
+ const url = parseValue(cssRuleStr, searchStartIndex, selector, delimiters);
+ if (url === null) {
+ break;
+ }
+ searchStartIndex = url.foundAtIndex + url.value.length;
+ if (mustEndWithQuote && url.value[url.value.length - 1] !== '"') continue;
+ const unquoted = removeQuotes(url.value);
+ if (!unquoted /* || (!unquoted.startsWith('http')&& !unquoted.startsWith("/") )*/ || unquoted === 'http://' || unquoted === 'https://') {
+ continue;
+ }
+
+ unquoted && urlsFound.push(unquoted);
+ }
+
+ return urlsFound;
+ };
+
+ /**
+ *
+ * @param {String} html
+ * @returns {String[]}
+ */
+ const getImageUrlsFromFromHtml = function (html) {
+ return getUrlsFromCssString(html, "src=", [' ', '>', '\t'], true);
+ };
+ const getSourceUrlsFromFromHtml = function (html) {
+ return getUrlsFromCssString(html, "source=", [' ', '>', '\t'], true);
+ };
+
+ /**
+ *
+ * @param {String} str
+ * @returns {String}
+ */
+ const removeQuotes = function (str) {
+ return str.replace(/["']/g, "");
+ };
+
+ const escapeRegExp = function (string) {
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+ };
+
+ /**
+ *
+ * @param {String} contentHtml
+ * @param {Number} width
+ * @param {Number} height
+ *
+ * @returns {Promise<String>}
+ */
+ const buildSvgDataUri = async function (webUrl, contentHtml, width, height, scroll) {
+
+ return new Promise(async function (resolve, reject) {
+
+ /* !! The problems !!
+ * 1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with)
+ * 2. Platform won't wait for external assets to load (fonts, images, etc.)
+ */
+
+ // copy styles
+ let cssStyles = "";
+ let urlsFoundInCss = [];
+
+ for (let i = 0; i < styleSheets.length; i++) {
+ try {
+ const rules = styleSheets[i].cssRules
+ for (let j = 0; j < rules.length; j++) {
+ const cssRuleStr = rules[j].cssText;
+ urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr));
+ cssStyles += cssRuleStr;
+ }
+ } catch (e) {
+
+ }
+ }
+
+ // const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss);
+ // for (let i = 0; i < fetchedResourcesFromStylesheets.length; i++) {
+ // const r = fetchedResourcesFromStylesheets[i];
+ // if (r.resourceUrl) {
+ // cssStyles = cssStyles.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64);
+ // }
+ // }
+
+ contentHtml = contentHtml.replace(/<source[^>]*>/g, "") // <picture> tags have a <source> which has a srcset field of image refs. instead of converting each, just use the default <img> of the picture
+ .replace(/noscript/g, "div").replace(/<div class="mediaset"><\/div>/g, "") // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s. But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
+ .replace(/<link[^>]*>/g, "") // don't need to keep any linked style sheets because we've already processed all style sheets above
+ .replace(/srcset="([^ "]*)[^"]*"/g, "src=\"$1\""); // instead of converting each item in the srcset to a data url, just convert the first one and use that
+ let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml);
+ const fetchedResources = await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml);
+ for (let i = 0; i < fetchedResources.length; i++) {
+ const r = fetchedResources[i];
+ if (r.resourceUrl) {
+ contentHtml = contentHtml.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64);
+ }
+ }
+
+ const styleElem = document.createElement("style");
+ styleElem.innerHTML = cssStyles.replace("&gt;", ">").replace("&lt;", "<");
+
+ const styleElemString = new XMLSerializer().serializeToString(styleElem).replace(/&gt;/g, ">").replace(/&lt;/g, "<");
+
+ // create DOM element string that encapsulates styles + content
+ const contentRootElem = document.createElement("body");
+ contentRootElem.style.zIndex = "1111";
+ // contentRootElem.style.transform = "scale(0.08)"
+ contentRootElem.innerHTML = styleElemString + contentHtml;
+ contentRootElem.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
+ //document.body.appendChild(contentRootElem);
+
+ const contentRootElemString = new XMLSerializer().serializeToString(contentRootElem);
+
+ // build SVG string
+ const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='${width}' height='${height}'>
+ <foreignObject x='0' y='${-scroll}' width='${width}' height='${scroll + height}'>
+ ${contentRootElemString}
+ </foreignObject>
+ </svg>`;
+
+ // convert SVG to data-uri
+ const dataUri = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svg)))}`;
+
+ resolve(dataUri);
+ });
+ };
+
+ /**
+ * @param {String} html
+ * @param {Number} width
+ * @param {Number} height
+ *
+ * @return {Promise<Image>}
+ */
+ this.renderToImage = async function (webUrl, html, width, height, scroll) {
+ return new Promise(async function (resolve, reject) {
+ const img = new Image();
+ console.log("BUILDING SVG for:" + webUrl);
+ img.src = await buildSvgDataUri(webUrl, html, width, height, scroll);
+
+ img.onload = function () {
+ console.log("IMAGE SVG created:" + webUrl);
+ resolve(img);
+ };
+ });
+ };
+
+ /**
+ * @param {String} html
+ * @param {Number} width
+ * @param {Number} height
+ *
+ * @return {Promise<Image>}
+ */
+ this.renderToCanvas = async function (webUrl, html, width, height, scroll) {
+ return new Promise(async function (resolve, reject) {
+ const img = await self.renderToImage(webUrl, html, width, height, scroll);
+
+ const canvas = document.createElement('canvas');
+ canvas.width = img.width;
+ canvas.height = img.height;
+
+ const canvasCtx = canvas.getContext('2d');
+ canvasCtx.drawImage(img, 0, 0, img.width, img.height);
+
+ resolve(canvas);
+ });
+ };
+
+ /**
+ * @param {String} html
+ * @param {Number} width
+ * @param {Number} height
+ *
+ * @return {Promise<String>}
+ */
+ this.renderToBase64Png = async function (webUrl, html, width, height, scroll) {
+ return new Promise(async function (resolve, reject) {
+ const canvas = await self.renderToCanvas(webUrl, html, width, height, scroll);
+ resolve(canvas.toDataURL('image/png'));
+ });
+ };
+
+};
+
+
+export function CreateImage(webUrl, styleSheets, html, width, height, scroll) {
+ const val = (new ForeignHtmlRenderer(styleSheets)).renderToBase64Png(webUrl, html.replace(/\n/g, "").replace(/<script((?!\/script).)*<\/script>/g, ""), width, height, scroll);
+ return val;
+}
+
+
+
+var ClipboardUtils = new function () {
+ var permissions = {
+ 'image/bmp': true,
+ 'image/gif': true,
+ 'image/png': true,
+ 'image/jpeg': true,
+ 'image/tiff': true
+ };
+
+ function getType(types) {
+ for (var j = 0; j < types.length; ++j) {
+ var type = types[j];
+ if (permissions[type]) {
+ return type;
+ }
+ }
+ return null;
+ }
+ function getItem(items) {
+ for (var i = 0; i < items.length; ++i) {
+ var item = items[i];
+ if (item) {
+ var type = getType(item.types);
+ if (type) {
+ return item.getType(type);
+ }
+ }
+ }
+ return null;
+ }
+ function loadFile(file, callback) {
+ if (window.FileReader) {
+ var reader = new FileReader();
+ reader.onload = function () {
+ callback(reader.result, null);
+ };
+ reader.onerror = function () {
+ callback(null, 'Incorrect file.');
+ };
+ reader.readAsDataURL(file);
+ } else {
+ callback(null, 'File api is not supported.');
+ }
+ }
+ this.readImage = function (callback) {
+ if (navigator.clipboard) {
+ var promise = navigator.clipboard.read();
+ promise
+ .then(function (items) {
+ var promise = getItem(items);
+ if (promise == null) {
+ callback(null, null);
+ return;
+ }
+ promise
+ .then(function (result) {
+ loadFile(result, callback);
+ })
+ .catch(function (error) {
+ callback(null, 'Reading clipboard error.');
+ });
+ })
+ .catch(function (error) {
+ callback(null, 'Reading clipboard error.');
+ });
+ } else {
+ callback(null, 'Clipboard is not supported.');
+ }
+ };
+};
+
+
+export function pasteImageBitmap(callback) {
+ return ClipboardUtils.readImage(callback);
+} \ No newline at end of file
diff --git a/src/client/views/nodes/button/ButtonScripts.ts b/src/client/views/nodes/button/ButtonScripts.ts
index bb4dd8bc9..f3731b8f9 100644
--- a/src/client/views/nodes/button/ButtonScripts.ts
+++ b/src/client/views/nodes/button/ButtonScripts.ts
@@ -1,14 +1,14 @@
-import { Scripting } from "../../../util/Scripting";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
import { SelectionManager } from "../../../util/SelectionManager";
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeView(view: string) {
+ScriptingGlobals.add(function changeView(view: string) {
const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function toggleOverlay() {
+ScriptingGlobals.add(function toggleOverlay() {
const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("failed");
}); \ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index bd103dcf7..ca13590de 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,31 +5,30 @@ import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ColorState, SketchPicker } from 'react-color';
-import { Doc, StrListCast, WidthSym, HeightSym } from '../../../../fields/Doc';
+import { Doc, StrListCast } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
-import { createSchema, makeInterface } from '../../../../fields/Schema';
+import { createSchema } from '../../../../fields/Schema';
import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { WebField } from '../../../../fields/URLField';
+import { Utils } from '../../../../Utils';
import { DocumentType } from '../../../documents/DocumentTypes';
-import { Scripting } from "../../../util/Scripting";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
import { SelectionManager } from '../../../util/SelectionManager';
-import { UndoManager, undoBatch } from '../../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { CollectionViewType } from '../../collections/CollectionView';
import { ContextMenu } from '../../ContextMenu';
import { DocComponent } from '../../DocComponent';
import { EditableView } from '../../EditableView';
import { GestureOverlay } from '../../GestureOverlay';
import { Colors } from '../../global/globalEnums';
-import { SetActiveInkColor, ActiveFillColor, SetActiveFillColor, ActiveInkWidth, ActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '.././FieldView';
import { RichTextMenu } from '../formattedText/RichTextMenu';
-import { Utils } from '../../../../Utils';
-import { IButtonProps } from './ButtonInterface';
+import { WebBox } from '../WebBox';
import { FontIconBadge } from './FontIconBadge';
import './FontIconBox.scss';
-import { WebBox } from '../WebBox';
const FontIconSchema = createSchema({
icon: "string",
});
@@ -57,11 +56,8 @@ export enum NumButtonType {
export interface ButtonProps extends FieldViewProps {
type?: ButtonType;
}
-
-type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
-const FontIconDocument = makeInterface(FontIconSchema);
@observer
-export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(FontIconDocument) {
+export class FontIconBox extends DocComponent<ButtonProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
showTemplate = (): void => {
const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
@@ -105,16 +101,11 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
*/
@computed get numberButton() {
const numBtnType: string = StrCast(this.rootDoc.numBtnType);
- const setValue = (value: number) => {
- // Script for running the toggle
- const script: string = StrCast(this.rootDoc.script) + "(" + value + ")";
- ScriptField.MakeScript(script)?.script.run();
- };
+ const numScript = ScriptCast(this.rootDoc.script);
+ const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false });
// Script for checking the outcome of the toggle
- const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)";
- const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result || 0;
-
+ const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
if (numBtnType === NumButtonType.Slider) {
const dropdown =
@@ -237,34 +228,37 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const script: string = StrCast(this.rootDoc.script);
+ const script = ScriptCast(this.rootDoc.script);
let noviceList: string[] = [];
let text: string | undefined;
let dropdown = true;
let icon: IconProp = "caret-down";
-
- if (script === 'setView') {
- const selected = SelectionManager.Docs().lastElement();
- if (selected) {
- if (StrCast(selected.type) === DocumentType.COL) {
- text = StrCast(selected._viewType);
+ try {
+ if (script.script.originalScript.startsWith('setView')) {
+ const selected = SelectionManager.Docs().lastElement();
+ if (selected) {
+ if (StrCast(selected.type) === DocumentType.COL) {
+ text = StrCast(selected._viewType);
+ } else {
+ dropdown = false;
+ text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
+ icon = Doc.toIcon(selected);
+ }
} else {
dropdown = false;
- text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
- icon = Doc.toIcon(selected);
+ icon = "globe-asia";
+ text = "User Default";
}
- } else {
- dropdown = false;
- icon = "globe-asia";
- text = "User Default";
+ noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
+ } else if (script.script.originalScript.startsWith('setFont')) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+ text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+ noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
+ "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
}
- noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
- } else if (script === 'setFont') {
- const editorView = RichTextMenu.Instance?.TextView?.EditorView;
- text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
- noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
- "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
+ } catch (e) {
+ console.log(e);
}
// Get items to place into the list
@@ -272,18 +266,12 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
return;
}
- const click = () => {
- const s = ScriptField.MakeScript(script + '("' + value + '")');
- if (s) {
- s.script.run().result;
- }
- };
return <div className="list-item" key={`${value}`}
style={{
- fontFamily: script === 'setFont' ? value : undefined,
+ fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined,
backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
}}
- onClick={click}>
+ onClick={() => script.script.run({ value }).result}>
{value[0].toUpperCase() + value.slice(1)}
</div>;
});
@@ -319,15 +307,12 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
}
@observable colorPickerClosed: boolean = true;
- @computed get colorScript() {
- const script = StrCast(this.rootDoc.script);
- return ScriptField.MakeScript(script + '(colValue, checkResult)', { colValue: "string", checkResult: "boolean" });
- }
+ @computed get colorScript() { return ScriptCast(this.rootDoc.script); }
colorPicker = (curColor: string) => {
const change = (value: ColorState) => {
const s = this.colorScript;
- s && undoBatch(() => s.script.run({ colValue: Utils.colorString(value), checkResult: false }).result)();
+ s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)();
};
const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -343,7 +328,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
@computed get colorButton() {
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const curColor = this.colorScript?.script.run({ colValue: undefined, checkResult: true }).result ?? "transparent";
+ const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
<div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
@@ -439,52 +424,34 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
@computed get editableText() {
// Script for running the toggle
- const script: string = StrCast(this.rootDoc.script);
-
- // Script for checking the outcome of the toggle
- const checkScript: string = StrCast(this.rootDoc.script) + "('', true)";
-
+ const script = ScriptCast(this.rootDoc.script);
// Function to run the script
- const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
+ const checkResult = script?.script.run({ value: "", _readOnly_: true }).result;
- const setValue = (value: string, shiftDown?: boolean): boolean => {
- ScriptField.MakeScript(script + "('" + value + "')")?.script.run();
- return true;
- };
+ const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result;
return (
<div className="menuButton editableText">
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
<div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}>
- <EditableView GetValue={() => checkResult} SetValue={setValue} contents={checkResult} />
+ <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} />
</div>
</div>
);
}
-
render() {
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-
const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
- <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+ <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
{this.label}
</div>;
const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
- <div className="fontIconBox-label" style={{ color: color, backgroundColor: "transparent" }}>
+ <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
{this.label}
</div>;
- const buttonProps: IButtonProps = {
- type: this.type,
- rootDoc: this.rootDoc,
- label: label,
- backgroundColor: backgroundColor,
- icon: this.icon,
- color: color
- };
-
const buttonText = StrCast(this.rootDoc.buttonText);
// TODO:glr Add label of button type
@@ -493,7 +460,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
switch (this.type) {
case ButtonType.TextButton:
button = (
- <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
+ <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
{buttonText ?
<div className="button-text">
@@ -522,7 +489,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
break;
case ButtonType.ToolButton:
button = (
- <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}>
+ <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
{label}
</div>
@@ -534,7 +501,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
break;
case ButtonType.ClickButton:
button = (
- <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1 }}>
+ <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
{label}
</div>
@@ -544,7 +511,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
const trailsIcon = <img src={`/assets/${"presTrails.png"}`}
style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
button = (
- <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
+ <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
{this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
{menuLabel}
<FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
@@ -564,14 +531,14 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setView(view: string) {
+ScriptingGlobals.add(function setView(view: string) {
const selected = SelectionManager.Docs().lastElement();
selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
if (checkResult) {
return selected?._backgroundColor ?? "transparent";
@@ -580,7 +547,7 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
if (checkResult) {
return Doc.SharingDoc().userColor;
}
@@ -590,7 +557,7 @@ Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolea
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
if (checkResult && selected) {
if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
@@ -612,7 +579,7 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
**/
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) {
SelectionManager.Docs().map(doc => doc._fontFamily = font);
const editorView = RichTextMenu.Instance.TextView?.EditorView;
if (checkResult) {
@@ -622,7 +589,7 @@ Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
else Doc.UserDoc().fontFamily = font;
});
-Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
+ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
const editorView = RichTextMenu.Instance.TextView?.EditorView;
const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
switch (info) {
@@ -633,7 +600,7 @@ Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color"
}
});
-Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
+ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent";
@@ -642,7 +609,7 @@ Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", ch
else Doc.UserDoc().textAlign = align;
});
-Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
+ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
@@ -657,8 +624,8 @@ Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", check
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
- const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
@@ -669,7 +636,7 @@ Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean)
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
const editorView = RichTextMenu.Instance.TextView?.EditorView;
@@ -686,18 +653,18 @@ Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: bool
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFontSize(size: string | number, checkResult?: boolean) {
- if (typeof size === "number") size = size.toString();
- if (size && Number(size).toString() === size) size += "px";
- const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", "");
}
+ if (typeof size === "number") size = size.toString();
+ if (size && Number(size).toString() === size) size += "px";
if (editorView) RichTextMenu.Instance.setFontSize(size);
else Doc.UserDoc()._fontSize = size;
});
-Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent";
@@ -706,7 +673,7 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold";
});
-Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent";
@@ -715,7 +682,7 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline";
});
-Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent";
@@ -733,7 +700,7 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
* setStrokeColor
**/
-Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
if (checkResult) {
return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
Colors.MEDIUM_BLUE : "transparent";
@@ -759,7 +726,7 @@ Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolea
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFillColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
if (checkResult) {
if (selected?.type === DocumentType.INK) {
@@ -771,7 +738,7 @@ Scripting.addGlobal(function setFillColor(color?: string, checkResult?: boolean)
SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color);
});
-Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
+ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) {
if (checkResult) {
const selected = SelectionManager.Docs().lastElement();
if (selected?.type === DocumentType.INK) {
@@ -784,7 +751,7 @@ Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean
});
// toggle: Set overlay status of selected document
-Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boolean) {
if (checkResult) {
const selected = SelectionManager.Docs().lastElement();
if (selected?.type === DocumentType.INK) {
@@ -800,7 +767,7 @@ Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolea
/** WEB
* webSetURL
**/
-Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
+ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
const selected = SelectionManager.Views().lastElement();
if (selected?.rootDoc.type === DocumentType.WEB) {
if (checkResult) {
@@ -810,24 +777,26 @@ Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
//selected.rootDoc.data = new WebField(url);
}
});
-Scripting.addGlobal(function webForward() {
- const selected = SelectionManager.Views().lastElement();
- if (selected?.rootDoc.type === DocumentType.WEB) {
- (selected.ComponentView as WebBox).forward();
+ScriptingGlobals.add(function webForward(checkResult?: boolean) {
+ const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
+ if (checkResult) {
+ return selected?.forward(checkResult) ? undefined : "lightGray";
}
+ selected?.forward();
});
-Scripting.addGlobal(function webBack() {
- const selected = SelectionManager.Views().lastElement();
- if (selected?.rootDoc.type === DocumentType.WEB) {
- (selected.ComponentView as WebBox).back();
+ScriptingGlobals.add(function webBack(checkResult?: boolean) {
+ const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
+ if (checkResult) {
+ return selected?.back(checkResult) ? undefined : "lightGray";
}
+ selected?.back();
});
/** Schema
* toggleSchemaPreview
**/
-Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
if (checkResult && selected) {
const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
@@ -846,7 +815,7 @@ Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
/** STACK
* groupBy
*/
-Scripting.addGlobal(function setGroupBy(key: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
SelectionManager.Docs().map(doc => doc._fontFamily = key);
const editorView = RichTextMenu.Instance.TextView?.EditorView;
if (checkResult) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a47395771..391d84cfa 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,13 +12,11 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
import { EditorView } from "prosemirror-view";
import { DateField } from '../../../../fields/DateField';
import { AclAdmin, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym, AclAugment } 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 { makeInterface } from "../../../../fields/Schema";
import { Cast, DateCast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
@@ -77,13 +75,10 @@ export interface FormattedTextBoxProps {
}
export const GoogleRef = "googleDocId";
-type RichTextDocument = makeInterface<[typeof documentSchema]>;
-const RichTextDocument = makeInterface(documentSchema);
-
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@observer
-export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps)>() {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); }
public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
public static Instance: FormattedTextBox;
@@ -645,7 +640,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
} else if (!this.rootDoc.isTemplateDoc) {
const title = StrCast(this.rootDoc.title);
this.rootDoc.title = "text";
- this.rootDoc.layout = (this.layoutDoc as Doc).layout as string;
+ this.rootDoc.layout = this.layoutDoc.layout as string;
this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
this.rootDoc.isTemplateDoc = false;
this.rootDoc.isTemplateForField = "";
@@ -1131,13 +1126,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
},
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
- dashComment(node, view, getPos) { return new DashDocCommentView(node, view, getPos); },
- dashDoc(node, view, getPos) { return new DashDocView(node, view, getPos, self); },
- dashField(node, view, getPos) { return new DashFieldView(node, view, getPos, self); },
- equation(node, view, getPos) { return new EquationView(node, view, getPos, self); },
- summary(node, view, getPos) { return new SummaryView(node, view, getPos); },
- ordered_list(node, view, getPos) { return new OrderedListView(); },
- footnote(node, view, getPos) { return new FootnoteView(node, view, getPos); }
+ dashComment(node: any, view: any, getPos: any) { return new DashDocCommentView(node, view, getPos); },
+ dashDoc(node: any, view: any, getPos: any) { return new DashDocView(node, view, getPos, self); },
+ dashField(node: any, view: any, getPos: any) { return new DashFieldView(node, view, getPos, self); },
+ equation(node: any, view: any, getPos: any) { return new EquationView(node, view, getPos, self); },
+ summary(node: any, view: any, getPos: any) { return new SummaryView(node, view, getPos); },
+ ordered_list(node: any, view: any, getPos: any) { return new OrderedListView(); },
+ footnote(node: any, view: any, getPos: any) { return new FootnoteView(node, view, getPos); }
},
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
@@ -1424,7 +1419,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.dataDoc[this.fieldKey + "-translation"] = result1 + "\r\n\r\n" + result[0];
}), 1000);
});
- } catch (e) { console.log(e.message); }
+ } catch (e: any) { console.log(e.message); }
this._lastText = curText;
}
}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 76a5675de..c76eda859 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -106,7 +106,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
bind("Mod-U", toggleEditableMark(schema.marks.underline));
//Commands for lists
- bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any));
+ bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
/// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
@@ -125,7 +125,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
dispatch(tx3);
})) { // couldn't sink into an existing list, so wrap in a new one
const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
- if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => {
+ if (!wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
const tx3 = updateBullets(tx2, schema);
// when promoting to a list, assume list will format things so don't copy the stored marks.
marks && tx3.ensureMarks([...marks]);
@@ -160,14 +160,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
});
//Commands to modify BlockType
- bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any)));
- bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any));
- bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any));
+ bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any)));
+ bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any));
+ bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any));
bind("Ctrl-m", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))));
for (let i = 1; i <= 6; i++) {
- bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch as any));
+ bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any));
}
//Command to create a horizontal break line
@@ -197,13 +197,13 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
if (!canEdit(state)) return true;
- if (!deleteSelection(state, (tx: Transaction<Schema<any, any>>) => {
+ if (!deleteSelection(state, (tx: Transaction<S>) => {
dispatch(updateBullets(tx, schema));
})) {
- if (!joinBackward(state, (tx: Transaction<Schema<any, any>>) => {
+ if (!joinBackward(state, (tx: Transaction<S>) => {
dispatch(updateBullets(tx, schema));
})) {
- if (!selectNodeBackward(state, (tx: Transaction<Schema<any, any>>) => {
+ if (!selectNodeBackward(state, (tx: Transaction<S>) => {
dispatch(updateBullets(tx, schema));
})) {
return false;
@@ -225,14 +225,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
const depth = trange ? liftTarget(trange) : undefined;
const split = path.length > 5 && !path[path.length - 3].textContent && path[path.length - 6].type !== schema.nodes.list_item;
if (split && trange && depth !== undefined && depth !== null) {
- dispatch(state.tr.lift(trange, depth));
+ dispatch(state.tr.lift(trange, depth) as any);
return true;
}
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
const cr = state.selection.$from.node().textContent.endsWith("\n");
- if (cr || !newlineInCode(state, dispatch)) {
- if (!splitListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
+ if (cr || !newlineInCode(state, dispatch as any)) {
+ if (!splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => {
const tx3 = updateBullets(tx2, schema);
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 711136469..bafae84dc 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -34,13 +34,9 @@ export class RichTextRules {
wrappingInputRule(
/^1\.\s$/,
schema.nodes.ordered_list,
- () => {
- return ({ mapStyle: "decimal", bulletStyle: 1 });
- },
- (match: any, node: any) => {
- return node.childCount + node.attrs.order === +match[1];
- },
- (type: any) => ({ type: type, attrs: { mapStyle: "decimal", bulletStyle: 1 } })
+ () => ({ mapStyle: "decimal", bulletStyle: 1 }),
+ (match: any, node: any) => node.childCount + node.attrs.order === +match[1],
+ ((type: any) => ({ type: type, attrs: { mapStyle: "decimal", bulletStyle: 1 } })) as any
),
// A. create alphabetical ordered list
@@ -55,20 +51,16 @@ export class RichTextRules {
(match: any, node: any) => {
return node.childCount + node.attrs.order === +match[1];
},
- (type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })
+ ((type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })) as any
),
// * + - create bullet list
wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.ordered_list,
// match => {
- () => {
- return ({ mapStyle: "bullet" });
- // return ({ order: +match[1] })
- },
- (match: any, node: any) => {
- return node.childCount + node.attrs.order === +match[1];
- },
- (type: any) => ({ type: type, attrs: { mapStyle: "bullet" } })),
+ () => ({ mapStyle: "bullet" }), // ({ order: +match[1] })
+ (match: any, node: any) => node.childCount + node.attrs.order === +match[1],
+ ((type: any) => ({ type: type, attrs: { mapStyle: "bullet" } })) as any
+ ),
// ``` create code block
textblockTypeInputRule(/^```$/, schema.nodes.code_block),
@@ -221,7 +213,7 @@ export class RichTextRules {
tr.deleteRange(start, end).replaceSelectionWith(newNode); // replace insertion with a footnote.
return tr.setSelection(new NodeSelection( // select the footnote node to open its display
tr.doc.resolve( // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node)
- tr.selection.anchor - tr.selection.$anchor.nodeBefore!.nodeSize)));
+ tr.selection.anchor - (tr.selection.$anchor.nodeBefore?.nodeSize || 0))));
}),
// activate a style by name using prefix '%<color name>'
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 14d6e8be6..9b74bb618 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -6,11 +6,10 @@ import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc";
-import { documentSchema } from "../../../../fields/documentSchemas";
import { InkTool } from "../../../../fields/InkField";
import { List } from "../../../../fields/List";
import { PrefetchProxy } from "../../../../fields/Proxy";
-import { listSpec, makeInterface } from "../../../../fields/Schema";
+import { listSpec } from "../../../../fields/Schema";
import { ScriptField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils';
@@ -18,7 +17,7 @@ import { Docs } from "../../../documents/Documents";
import { DocumentType } from "../../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { DocumentManager } from "../../../util/DocumentManager";
-import { Scripting } from "../../../util/Scripting";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
import { SelectionManager } from "../../../util/SelectionManager";
import { undoBatch, UndoManager } from "../../../util/UndoManager";
import { CollectionDockingView } from "../../collections/CollectionDockingView";
@@ -39,11 +38,8 @@ export class PinProps {
hidePresBox?: boolean;
}
-type PresBoxSchema = makeInterface<[typeof documentSchema]>;
-const PresBoxDocument = makeInterface(documentSchema);
-
@observer
-export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
+export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
/**
@@ -2472,7 +2468,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>;
}
}
-Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
+ScriptingGlobals.add(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data);
if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 35 : 31;
if (field === 'presStatus') return container.presStatus;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 238d025dc..a4ec559f5 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -3,11 +3,10 @@ import { Tooltip } from "@material-ui/core";
import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
import { DataSym, Doc, Opt } from "../../../../fields/Doc";
-import { documentSchema } from '../../../../fields/documentSchemas';
import { Id } from "../../../../fields/FieldSymbols";
-import { createSchema, makeInterface } from '../../../../fields/Schema';
import { Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
+import { DocUtils } from "../../../documents/Documents";
import { DocumentType } from "../../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { DocumentManager } from "../../../util/DocumentManager";
@@ -16,36 +15,20 @@ import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
import { ViewBoxBaseComponent } from '../../DocComponent';
import { EditableView } from "../../EditableView";
+import { Colors } from "../../global/globalEnums";
import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView";
import { FieldView, FieldViewProps } from '../../nodes/FieldView';
-import { PresBox } from "./PresBox";
-import { Colors } from "../../global/globalEnums";
import { StyleProp } from "../../StyleProvider";
+import { PresBox } from "./PresBox";
import "./PresElementBox.scss";
-import React = require("react");
-import { DocUtils } from "../../../documents/Documents";
import { PresMovement } from "./PresEnums";
-
-export const presSchema = createSchema({
- presentationTargetDoc: Doc,
- presBox: Doc,
- presZoomButton: "boolean",
- presNavButton: "boolean",
- presHideTillShownButton: "boolean",
- presFadeButton: "boolean",
- presHideAfterButton: "boolean",
- presGroupButton: "boolean",
- presExpandInlineButton: "boolean"
-});
-
-type PresDocument = makeInterface<[typeof presSchema, typeof documentSchema]>;
-const PresDocument = makeInterface(presSchema, documentSchema);
+import React = require("react");
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
*/
@observer
-export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDocument>(PresDocument) {
+export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
_heightDisposer: IReactionDisposer | undefined;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 3f7f38bdf..1856c5353 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -26,7 +26,6 @@ import { StyleProp } from "../StyleProvider";
import { AnchorMenu } from "./AnchorMenu";
import { Annotation } from "./Annotation";
import "./PDFViewer.scss";
-const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import React = require("react");
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
@@ -34,7 +33,7 @@ const _global = (window /* browser */ || global /* node */) as any;
//pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
// The workerSrc property shall be specified.
-pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.4.456/build/pdf.worker.min.js";
+pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.13.216/build/pdf.worker.js";
interface IViewerProps extends FieldViewProps {
Document: Doc;
@@ -159,7 +158,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
initialLoad = async () => {
if (this._pageSizes.length === 0) {
this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
- await Promise.all(this._pageSizes.map<Pdfjs.PDFPromise<any>>((val, i) =>
+ await Promise.all(this._pageSizes.map((val, i) =>
this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => {
const page0or180 = page.rotate === 0 || page.rotate === 180;
this._pageSizes.splice(i, 1, {
@@ -219,7 +218,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
}
pagesinit = () => {
- if (this._pdfViewer._setDocumentViewerElement.offsetParent) {
+ if (this._pdfViewer._setDocumentViewerElement?.offsetParent) {
runInAction(() => this._pdfViewer.currentScaleValue = this._zoomed = 1);
this.gotoPage(NumCast(this.props.Document._curPage, 1));
}
@@ -555,7 +554,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
</div>;
}
@computed get pdfViewerDiv() {
- return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && (this.props.isSelected() || this.props.isContentActive()) ? "-selected" : "")} ref={this._viewer} />;
+ return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && this.props.isContentActive() ? "-selected" : "")} ref={this._viewer} />;
}
@computed get contentScaling() { return this.props.ContentScaling?.() || 1; }
@computed get standinViews() {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index d23aa1d3b..eaa537596 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,28 +1,18 @@
+import { Tooltip } from "@material-ui/core";
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc';
-import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from '../../../fields/FieldSymbols';
-import { createSchema, makeInterface } from '../../../fields/Schema';
import { StrCast } from '../../../fields/Types';
+import { DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentManager } from '../../util/DocumentManager';
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./SearchBox.scss";
-import { DocumentManager } from '../../util/DocumentManager';
-import { DocUtils } from '../../documents/Documents';
-import { Tooltip } from "@material-ui/core";
-import { DictationOverlay } from '../DictationOverlay';
-import { CollectionSchemaBooleanCell } from '../collections/collectionSchema/CollectionSchemaCells';
-
-export const searchSchema = createSchema({
- Document: Doc
-});
-type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
-const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
const DAMPENING_FACTOR = 0.9;
const MAX_ITERATIONS = 25;
@@ -38,7 +28,7 @@ export interface SearchBoxProps extends FieldViewProps {
* the search panel on the left side of the screen.
*/
@observer
-export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDocument>(SearchBoxDocument) {
+export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
public static Instance: SearchBox;
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 505a8da7e..e0d328c89 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -12,6 +12,7 @@ import "./DashWebRTCVideo.scss";
import { hangup, initialize, refreshVideos } from "./WebCamLogic";
import React = require("react");
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
/**
@@ -64,8 +65,8 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
}}></video>
</div>
<div className="buttonContainer">
- <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash} color="white" /></div>
- <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync} color="white" /></div>
+ <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" /></div>
+ <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync as IconLookup} color="white" /></div>
</div>
</div >;
diff --git a/src/fields/CursorField.ts b/src/fields/CursorField.ts
index 28467377b..a8a2859d2 100644
--- a/src/fields/CursorField.ts
+++ b/src/fields/CursorField.ts
@@ -50,7 +50,7 @@ export default class CursorField extends ObjectField {
setPosition(position: CursorPosition) {
this.data.position = position;
this.data.metadata.timestamp = Date.now();
- this[OnUpdate]();
+ this[OnUpdate]?.();
}
[Copy]() {
diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts
index 48106d978..26f51b2d3 100644
--- a/src/fields/DateField.ts
+++ b/src/fields/DateField.ts
@@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
import { serializable, date } from "serializr";
import { ObjectField } from "./ObjectField";
import { Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { scriptingGlobal, Scripting } from "../client/util/Scripting";
+import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals";
@scriptingGlobal
@Deserializable("date")
@@ -35,6 +35,6 @@ export class DateField extends ObjectField {
}
}
-Scripting.addGlobal(function d(...dateArgs: any[]) {
+ScriptingGlobals.add(function d(...dateArgs: any[]) {
return new DateField(new (Date as any)(...dateArgs));
});
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 8a5491b4b..1253cf9c7 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -7,7 +7,7 @@ import { DocServer } from "../client/DocServer";
import { DocumentType } from "../client/documents/DocumentTypes";
import { CurrentUserUtils } from "../client/util/CurrentUserUtils";
import { LinkManager } from "../client/util/LinkManager";
-import { Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals";
import { SelectionManager } from "../client/util/SelectionManager";
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
import { UndoManager } from "../client/util/UndoManager";
@@ -216,7 +216,7 @@ export class Doc extends RefField {
return self.resolvedDataDoc && !self.isTemplateForField ? self :
Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
}
- @computed get __LAYOUT__() {
+ @computed get __LAYOUT__(): Doc | undefined {
const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
if (templateLayoutDoc) {
let renderFieldKey: any;
@@ -520,7 +520,8 @@ export namespace Doc {
Doc.SetLayout(alias, Doc.MakeAlias(layout));
}
alias.aliasOf = doc;
- alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`);
+ alias.aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
+ alias.title = ComputedField.MakeFunction(`renameAlias(this)`);
alias.author = Doc.CurrentUserEmail;
Doc.AddDocToList(Doc.GetProto(doc)[DataSym], "aliases", alias);
@@ -1390,33 +1391,33 @@ export namespace Doc {
}
-Scripting.addGlobal(function idToDoc(id: string): any { return DocServer.GetCachedRefField(id); });
-Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
-Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
-Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
-Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
-Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
-Scripting.addGlobal(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); });
-Scripting.addGlobal(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); });
-Scripting.addGlobal(function copyField(field: any) { return Field.Copy(field); });
-Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
-Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
-Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
-Scripting.addGlobal(function deiconifyView(doc: any) { Doc.deiconifyView(doc); });
-Scripting.addGlobal(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
-Scripting.addGlobal(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); });
-Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
-Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
-Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
-Scripting.addGlobal(function activePresentationItem() {
+ScriptingGlobals.add(function idToDoc(id: string): any { return DocServer.GetCachedRefField(id); });
+ScriptingGlobals.add(function renameAlias(doc: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${doc.aliasNumber})`; });
+ScriptingGlobals.add(function getProto(doc: any) { return Doc.GetProto(doc); });
+ScriptingGlobals.add(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
+ScriptingGlobals.add(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
+ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
+ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); });
+ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); });
+ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); });
+ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); });
+ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
+ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
+ScriptingGlobals.add(function deiconifyView(doc: any) { Doc.deiconifyView(doc); });
+ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
+ScriptingGlobals.add(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); });
+ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
+ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
+ScriptingGlobals.add(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
+ScriptingGlobals.add(function activePresentationItem() {
const curPres = Doc.UserDoc().activePresentation as Doc;
return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
});
-Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
+ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
const docs = SelectionManager.Views().map(dv => dv.props.Document).
filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP &&
(!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
return docs.length ? new List(docs) : prevValue;
});
-Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers: "match" | "check" | "x" | "remove") { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocRangeFilter(container: Doc, key: string, range: number[]) { Doc.setDocRangeFilter(container, key, range); });
+ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: any, modifiers: "match" | "check" | "x" | "remove") { Doc.setDocFilter(container, key, value, modifiers); });
+ScriptingGlobals.add(function setDocRangeFilter(container: Doc, key: string, range: number[]) { Doc.setDocRangeFilter(container, key, range); });
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 560cf3d63..31024e805 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -1,9 +1,9 @@
+import { Bezier } from "bezier-js";
import { createSimpleSchema, list, object, serializable } from "serializr";
-import { Scripting } from "../client/util/Scripting";
+import { ScriptingGlobals } from "../client/util/ScriptingGlobals";
import { Deserializable } from "../client/util/SerializationHelper";
import { Copy, ToScriptString, ToString } from "./FieldSymbols";
import { ObjectField } from "./ObjectField";
-import { Bezier } from "bezier-js";
// Helps keep track of the current ink tool in use.
export enum InkTool {
@@ -90,4 +90,4 @@ export class InkField extends ObjectField {
}
}
-Scripting.addGlobal("InkField", InkField); \ No newline at end of file
+ScriptingGlobals.add("InkField", InkField); \ No newline at end of file
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 93a8d1d60..60bf442d4 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -1,7 +1,7 @@
import { action, observable } from "mobx";
import { alias, list, serializable } from "serializr";
import { DocServer } from "../client/DocServer";
-import { Scripting } from "../client/util/Scripting";
+import { ScriptingGlobals } from "../client/util/ScriptingGlobals";
import { afterDocDeserialize, autoObject, Deserializable } from "../client/util/SerializationHelper";
import { Field } from "./Doc";
import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols";
@@ -327,8 +327,8 @@ class ListImpl<T extends Field> extends ObjectField {
export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any;
-Scripting.addGlobal("List", List);
-Scripting.addGlobal(function compareLists(l1: any, l2: any) {
+ScriptingGlobals.add("List", List);
+ScriptingGlobals.add(function compareLists(l1: any, l2: any) {
const L1 = Cast(l1, listSpec("string"), []);
const L2 = Cast(l2, listSpec("string"), []);
return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts
index 92b2cfa60..daa8a7777 100644
--- a/src/fields/ObjectField.ts
+++ b/src/fields/ObjectField.ts
@@ -1,9 +1,9 @@
import { RefField } from "./RefField";
import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
+import { ScriptingGlobals } from "../client/util/ScriptingGlobals";
export abstract class ObjectField {
- public [OnUpdate](diff?: any) { }
+ public [OnUpdate]?: (diff?: any) => void;
public [Parent]?: RefField | ObjectField;
abstract [Copy](): ObjectField;
@@ -17,4 +17,4 @@ export namespace ObjectField {
}
}
-Scripting.addGlobal(ObjectField); \ No newline at end of file
+ScriptingGlobals.add(ObjectField); \ No newline at end of file
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index f01b502c9..2c5f38818 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -6,7 +6,7 @@ import { DocServer } from "../client/DocServer";
import { RefField } from "./RefField";
import { ObjectField } from "./ObjectField";
import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
import { Plugins } from "./util";
function deserializeProxy(field: any) {
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index ae5f301d0..d7edd4266 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -1,8 +1,8 @@
-import { ObjectField } from "./ObjectField";
import { serializable } from "serializr";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
import { Deserializable } from "../client/util/SerializationHelper";
-import { Copy, ToScriptString, ToPlainText, ToString } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
+import { ObjectField } from "./ObjectField";
@scriptingGlobal
@Deserializable("RichTextField")
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index a53fa542e..3b02d0cfe 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
import { serializable, primitive } from "serializr";
import { ObjectField } from "./ObjectField";
import { Copy, ToScriptString, ToString, OnUpdate } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
import { ColumnType } from "../client/views/collections/collectionSchema/CollectionSchemaView";
export const PastelSchemaPalette = new Map<string, string>([
@@ -82,32 +82,32 @@ export class SchemaHeaderField extends ObjectField {
setHeading(heading: string) {
this.heading = heading;
- this[OnUpdate]();
+ this[OnUpdate]?.();
}
setColor(color: string) {
this.color = color;
- this[OnUpdate]();
+ this[OnUpdate]?.();
}
setType(type: ColumnType) {
this.type = type;
- this[OnUpdate]();
+ this[OnUpdate]?.();
}
setWidth(width: number) {
this.width = width;
- this[OnUpdate]();
+ this[OnUpdate]?.();
}
setDesc(desc: boolean | undefined) {
this.desc = desc;
- this[OnUpdate]();
+ this[OnUpdate]?.();
}
setCollapsed(collapsed: boolean | undefined) {
this.collapsed = collapsed;
- this[OnUpdate]();
+ this[OnUpdate]?.();
}
[Copy]() {
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index bd93bf5fb..40ca0ce22 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -1,15 +1,16 @@
-import { ObjectField } from "./ObjectField";
-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, Opt } from "./Doc";
-import { Plugins, setter } from "./util";
import { computedFn } from "mobx-utils";
+import { createSimpleSchema, custom, map, object, primitive, PropSchema, serializable, SKIP } from "serializr";
+import { CompiledScript, CompileScript } from "../client/util/Scripting";
+import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals";
+import { autoObject, Deserializable } from "../client/util/SerializationHelper";
+import { numberRange } from "../Utils";
+import { Doc, Field, Opt } from "./Doc";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
+import { List } from "./List";
+import { ObjectField } from "./ObjectField";
import { ProxyField } from "./Proxy";
import { Cast, NumCast } from "./Types";
-import { List } from "./List";
-import { numberRange } from "../Utils";
+import { Plugins } from "./util";
function optional(propSchema: PropSchema) {
return custom(value => {
@@ -40,28 +41,9 @@ const scriptSchema = createSimpleSchema({
});
async function deserializeScript(script: ScriptField) {
- if (script.script.originalScript === 'copyDragFactory(this.dragFactory)') {
- return (script as any).script = (ScriptField.GetCopyOfDragFactory ?? (ScriptField.GetCopyOfDragFactory = ScriptField.MakeFunction('copyDragFactory(this.dragFactory)')))?.script;
- }
- if (script.script.originalScript === 'links(self)') {
- return (script as any).script = (ScriptField.LinksSelf ?? (ScriptField.LinksSelf = ComputedField.MakeFunction('links(self)')))?.script;
- }
- if (script.script.originalScript === 'openOnRight(copyDragFactory(this.dragFactory))') {
- return (script as any).script = (ScriptField.OpenOnRight ?? (ScriptField.OpenOnRight = ComputedField.MakeFunction('openOnRight(copyDragFactory(this.dragFactory))')))?.script;
- }
- if (script.script.originalScript === 'deiconifyView(self)') {
- return (script as any).script = (ScriptField.DeiconifyView ?? (ScriptField.DeiconifyView = ComputedField.MakeFunction('deiconifyView(self)')))?.script;
- }
- if (script.script.originalScript === 'convertToButtons(dragData)') {
- return (script as any).script = (ScriptField.ConvertToButtons ?? (ScriptField.ConvertToButtons = ComputedField.MakeFunction('convertToButtons(dragData)', { dragData: "DocumentDragData" })))?.script;
- }
- if (script.script.originalScript === 'IsNoviceMode()') {
- return (script as any).script = (ScriptField.NoviceMode ?? (ScriptField.NoviceMode = ComputedField.MakeFunction('IsNoviceMode()')))?.script;
- }
- if (script.script.originalScript === `selectMainMenu(self)`) {
- return (script as any).script = (ScriptField.SelectMenu ?? (ScriptField.SelectMenu = ComputedField.MakeFunction('selectMainMenu(self)')))?.script;
- }
const captures: ProxyField<Doc> = (script as any).captures;
+ const cache = captures ? undefined : ScriptField.GetScriptFieldCache(script.script.originalScript);
+ if (cache) return (script as any).script = cache;
if (captures) {
const doc = (await captures.value())!;
const captured: any = {};
@@ -75,6 +57,7 @@ async function deserializeScript(script: ScriptField) {
throw new Error("Couldn't compile loaded script");
}
(script as any).script = comp;
+ !captures && ScriptField._scriptFieldCache.set(script.script.originalScript, comp);
if (script.setterscript) {
const compset = CompileScript(script.setterscript?.originalScript, script.setterscript.options);
if (!compset.compiled) {
@@ -95,13 +78,9 @@ export class ScriptField extends ObjectField {
@serializable(autoObject())
private captures?: ProxyField<Doc>;
- public static GetCopyOfDragFactory: Opt<ScriptField>;
- public static LinksSelf: Opt<ScriptField>;
- public static OpenOnRight: Opt<ScriptField>;
- public static DeiconifyView: Opt<ScriptField>;
- public static ConvertToButtons: Opt<ScriptField>;
- public static NoviceMode: Opt<ScriptField>;
- public static SelectMenu: Opt<ScriptField>;
+ public static _scriptFieldCache: Map<string, Opt<CompiledScript>> = new Map();
+ public static GetScriptFieldCache(field: string) { return this._scriptFieldCache.get(field); }
+
constructor(script: CompiledScript, setterscript?: CompiledScript) {
super();
@@ -150,7 +129,13 @@ export class ScriptField extends ObjectField {
}
public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) {
const compiled = CompileScript(script, {
- params: { this: Doc?.name || "Doc", self: Doc?.name || "Doc", _last_: "any", ...params },
+ params: {
+ this: Doc?.name || "Doc", // this is the doc that executes the script
+ self: Doc?.name || "Doc", // self is the root doc of the doc that executes the script
+ _last_: "any", // _last_ is the previous value of a computed field when it is being triggered to re-run.
+ _readOnly_: "boolean", // _readOnly_ is set when a computed field is executed to indicate that it should not have mobx side-effects. used for checking the value of a set function (see FontIconBox)
+ ...params
+ },
typecheck: false,
editable: true,
addReturn: addReturn,
@@ -175,7 +160,7 @@ export class ComputedField extends ScriptField {
_lastComputedResult: any;
//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;
+ _valueOutsideReaction = (doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult, _readOnly_: true }, console.log).result;
[Copy](): ObjectField {
@@ -201,19 +186,6 @@ export class ComputedField extends ScriptField {
return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
}
}
-Scripting.addGlobal(function setIndexVal(list: any[], index: number, value: any) {
- while (list.length <= index) list.push(undefined);
- list[index] = value;
-}, "sets the value at a given index of a list", "(list: any[], index: number, value: any)");
-
-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);
-}, "returns the value at a given index of a list", "(list: any[], index: number)");
-
-Scripting.addGlobal(function makeScript(script: string) {
- return ScriptField.MakeScript(script);
-}, "returns the value at a given index of a list", "(list: any[], index: number)");
-
export namespace ComputedField {
let useComputed = true;
export function DisableComputedFields() {
@@ -242,4 +214,17 @@ export namespace ComputedField {
}
});
}
-} \ No newline at end of file
+}
+
+ScriptingGlobals.add(function setIndexVal(list: any[], index: number, value: any) {
+ while (list.length <= index) list.push(undefined);
+ list[index] = value;
+}, "sets the value at a given index of a list", "(list: any[], index: number, value: any)");
+
+ScriptingGlobals.add(function getIndexVal(list: any[], index: number) {
+ return list?.reduce((p, x, i) => (i <= index && x !== undefined) || p === undefined ? x : p, undefined as any);
+}, "returns the value at a given index of a list", "(list: any[], index: number)");
+
+ScriptingGlobals.add(function makeScript(script: string) {
+ return ScriptField.MakeScript(script);
+}, "returns the value at a given index of a list", "(list: any[], index: number)");
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 3d784448d..c90f3b6b3 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -3,6 +3,7 @@ import { List } from "./List";
import { RefField } from "./RefField";
import { DateField } from "./DateField";
import { ScriptField } from "./ScriptField";
+import { URLField, WebField, ImageField } from "./URLField";
export type ToType<T extends InterfaceValue> =
T extends "string" ? string :
@@ -91,6 +92,12 @@ export function DateCast(field: FieldResult) {
export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
return Cast(field, ScriptField, defaultVal);
}
+export function WebCast(field: FieldResult, defaultVal: WebField | null = null) {
+ return Cast(field, WebField, defaultVal);
+}
+export function ImageCast(field: FieldResult, defaultVal: ImageField | null = null) {
+ return Cast(field, ImageField, defaultVal);
+}
type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
@@ -104,5 +111,6 @@ export interface PromiseLike<T> {
then(callback: (field: Opt<T>) => void): void;
}
export function PromiseValue<T extends Field>(field: FieldResult<T>): PromiseLike<Opt<T>> {
- return field instanceof Promise ? field : { then(cb: ((field: Opt<T>) => void)) { return cb(field); } };
+ if (field instanceof Promise) return field as Promise<Opt<T>>;
+ return { then(cb: ((field: Opt<T>) => void)) { return cb(field); } };
} \ No newline at end of file
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index 4d3776a2c..1d4bbaed0 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
import { serializable, custom } from "serializr";
import { ObjectField } from "./ObjectField";
import { ToScriptString, ToString, Copy } from "./FieldSymbols";
-import { Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
import { Utils } from "../Utils";
function url() {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index d732a6e2f..cfcc48608 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -16,7 +16,7 @@ import * as React from "react";
import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
import { DocumentType } from "../client/documents/DocumentTypes";
import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { Scripting } from '../client/util/Scripting';
+import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
import { Transform } from '../client/util/Transform';
import { UndoManager } from "../client/util/UndoManager";
@@ -39,13 +39,13 @@ import "./AudioUpload.scss";
import "./ImageUpload.scss";
import "./MobileInterface.scss";
-library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
faTerminal, faToggleOn, 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, faBook, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft,
- faAlignLeft, faAlignRight);
+ faAlignLeft, faAlignRight].map(m => m as any));
@observer
@@ -669,19 +669,19 @@ export class MobileInterface extends React.Component {
//Global functions for mobile menu
-Scripting.addGlobal(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
+ScriptingGlobals.add(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
"opens the library to navigate through dashboards on Dash Mobile");
-Scripting.addGlobal(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
+ScriptingGlobals.add(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
"opens the upload files menu for Dash Mobile");
-Scripting.addGlobal(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
+ScriptingGlobals.add(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
"opens the mobile uploads collection on Dash Mobile");
-Scripting.addGlobal(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); },
+ScriptingGlobals.add(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); },
"opens the record and dictate menu on Dash Mobile");
-Scripting.addGlobal(function switchToMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); },
+ScriptingGlobals.add(function switchToMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); },
"opens the presentation on Dash Mobile");
-Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Instance.open(); },
+ScriptingGlobals.add(function openMobileSettings() { return SettingsManager.Instance.open(); },
"opens settings on Dash Mobile");
// Other global functions for mobile
-Scripting.addGlobal(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
+ScriptingGlobals.add(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
"changes the active document displayed on the Dash Mobile", "(doc: any)"); \ No newline at end of file
diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts
index 684c00c0d..ee8dd5b5b 100644
--- a/src/scraping/buxton/final/BuxtonImporter.ts
+++ b/src/scraping/buxton/final/BuxtonImporter.ts
@@ -282,7 +282,7 @@ export default async function executeImport(emitter: ResultCallback, terminator:
});
await transferAssets();
return parseFiles(wordDocuments, emitter, terminator);
- } catch (e) {
+ } catch (e: any) {
const message = [
"Unable to find a source directory.",
"Please ensure that the following directory exists:",
diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts
index d237869ed..bc8fd6f87 100644
--- a/src/server/ActionUtilities.ts
+++ b/src/server/ActionUtilities.ts
@@ -62,7 +62,7 @@ export async function log_execution<T>({ startMessage, endMessage, action, color
log_helper(`${startMessage}...`, resolvedColor);
try {
result = await action();
- } catch (e) {
+ } catch (e: any) {
error = e;
} finally {
log_helper(typeof endMessage === "string" ? endMessage : endMessage({ result, error }), resolvedColor);
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index 2b4212588..e419d3ac4 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -2,8 +2,7 @@ import ApiManager, { Registration } from "./ApiManager";
import { Method } from "../RouteManager";
import RouteSubscriber from "../RouteSubscriber";
import { existsSync, createReadStream, createWriteStream } from "fs";
-const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
-import * as Pdfjs from 'pdfjs-dist';
+import * as Pdfjs from 'pdfjs-dist/legacy/build/pdf';
import { createCanvas } from "canvas";
const imageSize = require("probe-image-size");
import * as express from "express";
@@ -84,7 +83,7 @@ async function CreateThumbnail(coreFilename: string, pageNum: number, res: expre
});
}
-function dispatchThumbnail(res: express.Response, { width, height }: Pdfjs.PDFPageViewport, thumbnailName: string) {
+function dispatchThumbnail(res: express.Response, { width, height }: Pdfjs.PageViewport, thumbnailName: string) {
res.send({
path: clientPathToFile(Directory.pdf_thumbnails, thumbnailName),
width,
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 02f6462aa..bfa07d47a 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -49,14 +49,17 @@ export default class UploadManager extends ApiManager {
subscription: "/uploadFormData",
secureHandler: async ({ req, res }) => {
const form = new formidable.IncomingForm();
- form.uploadDir = pathToDirectory(Directory.parsed_files);
form.keepExtensions = true;
+ form.uploadDir = pathToDirectory(Directory.parsed_files);
return new Promise<void>(resolve => {
form.parse(req, async (_err, _fields, files) => {
const results: Upload.FileResponse[] = [];
for (const key in files) {
- const result = await DashUploadUtils.upload(files[key]);
- result && !(result.result instanceof Error) && results.push(result);
+ const f = files[key];
+ if (!Array.isArray(f)) {
+ const result = await DashUploadUtils.upload(f);
+ result && !(result.result instanceof Error) && results.push(result);
+ }
}
_success(res, results);
resolve();
@@ -192,7 +195,8 @@ export default class UploadManager extends ApiManager {
let id: string = "";
try {
for (const name in files) {
- const path_2 = files[name].path;
+ const f = files[name];
+ const path_2 = Array.isArray(f) ? "" : f.path;
const zip = new AdmZip(path_2);
zip.getEntries().forEach((entry: any) => {
if (!entry.entryName.startsWith("files/")) return;
@@ -218,7 +222,7 @@ export default class UploadManager extends ApiManager {
id = getId(data.id);
const docs = Object.keys(datadocs).map(key => datadocs[key]);
docs.forEach(mapFn);
- await Promise.all(docs.map((doc: any) => new Promise(res => {
+ await Promise.all(docs.map((doc: any) => new Promise<void>(res => {
Database.Instance.replace(doc.id, doc, (err, r) => {
err && console.log(err);
res();
@@ -255,14 +259,15 @@ export default class UploadManager extends ApiManager {
secureHandler: ({ req, res }) => {
const uri = req.body.uri;
const filename = req.body.name;
+ const origSuffix = req.body.nosuffix ? SizeSuffix.None : SizeSuffix.Original;
if (!uri || !filename) {
res.status(401).send("incorrect parameters specified");
return;
}
- return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, SizeSuffix.Original))).then((savedName: string) => {
+ return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => {
const ext = extname(savedName).toLowerCase();
const { pngs, jpgs } = AcceptableMedia;
- const resizers = [
+ const resizers = !origSuffix ? [{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" }] : [
{ resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" },
{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" },
{ resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" },
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index fe80c6a7c..7be8a1e9f 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -22,7 +22,7 @@ export default class UserManager extends ApiManager {
secureHandler: async ({ res }) => {
const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, "users");
const results = await cursor.toArray();
- res.send(results.map(user => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
+ res.send(results.map((user: any) => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
}
});
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index 03ba33fee..1a5934d8f 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -214,7 +214,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
// indicate success or failure
mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(to)}`);
error && mainLog(red(error.message));
- } catch (error) {
+ } catch (error: any) {
mainLog(red("unable to dispatch zipped backup..."));
mainLog(red(error.message));
}
diff --git a/src/server/DashSession/Session/agents/monitor.ts b/src/server/DashSession/Session/agents/monitor.ts
index 0fdaf07ff..9cb5ab576 100644
--- a/src/server/DashSession/Session/agents/monitor.ts
+++ b/src/server/DashSession/Session/agents/monitor.ts
@@ -178,7 +178,7 @@ export class Monitor extends IPCMessageReceiver {
// ensure all necessary and no excess information is specified by the configuration file
validate(config, configurationSchema, options);
config = Utilities.preciseAssign({}, defaultConfig, config);
- } catch (error) {
+ } catch (error: any) {
if (error instanceof ValidationError) {
console.log(red("\nSession configuration failed."));
console.log("The given session.config.json configuration file is invalid.");
diff --git a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
index 95aa686e6..f6c8de521 100644
--- a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
+++ b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
@@ -157,7 +157,7 @@ export class PromisifiedIPCManager {
if (registered) {
results = await Promise.all(registered.map(handler => handler(args)));
}
- } catch (e) {
+ } catch (e: any) {
error = e;
}
if (!this.isDestroyed && this.target.send) {
diff --git a/src/server/DashSession/Session/agents/server_worker.ts b/src/server/DashSession/Session/agents/server_worker.ts
index 84d35b40e..634b0113d 100644
--- a/src/server/DashSession/Session/agents/server_worker.ts
+++ b/src/server/DashSession/Session/agents/server_worker.ts
@@ -138,7 +138,7 @@ export class ServerWorker extends IPCMessageReceiver {
this.isInitialized = true;
}
this.shouldServerBeResponsive = true;
- } catch (error) {
+ } catch (error: any) {
// if we expect the server to be unavailable, i.e. during compilation,
// the listening variable is false, activeExit will return early and the child
// process will continue
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index f13580865..552ab57a5 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -16,6 +16,7 @@ import { clientPathToFile, Directory, pathToDirectory, serverPathToFile } from '
import { resolvedServerUrl } from "./server_Initialization";
import { AcceptableMedia, Upload } from './SharedMediaTypes';
import request = require('request-promise');
+import formidable = require('formidable');
const { exec } = require("child_process");
const parse = require('pdf-parse');
const ffmpeg = require("fluent-ffmpeg");
@@ -25,7 +26,8 @@ export enum SizeSuffix {
Small = "_s",
Medium = "_m",
Large = "_l",
- Original = "_o"
+ Original = "_o",
+ None = ""
}
export function InjectSize(filename: string, size: SizeSuffix) {
@@ -69,7 +71,7 @@ export namespace DashUploadUtils {
else {
console.log(`stdout: ${stdout}`);
const data = { size: 0, path: videoId + ".mp4", name: videoId, type: "video/mp4" };
- const file = { ...data, toJSON: () => data };
+ const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ""), mtime: null, length: 0, mime: "", toJson: () => undefined as any }) };
res(MoveParsedFile(file, Directory.videos));
}
});
@@ -78,7 +80,7 @@ export namespace DashUploadUtils {
export async function upload(file: File): Promise<Upload.FileResponse> {
const { type, path, name } = file;
- const types = type.split("/");
+ const types = type?.split("/") ?? [];
const category = types[0];
let format = `.${types[1]}`;
@@ -242,7 +244,7 @@ export namespace DashUploadUtils {
// Use the request library to parse out file level image information in the headers
const { headers } = (await new Promise<any>((resolve, reject) => {
request.head(resolvedUrl, (error, res) => error ? reject(error) : resolve(res));
- }).catch(error => console.error(error)));
+ }).catch(console.error));
try {
// Compute the native width and height ofthe image with an npm module
const { width: nativeWidth, height: nativeHeight } = await requestImageSize(resolvedUrl);
@@ -256,7 +258,7 @@ export namespace DashUploadUtils {
filename,
...results
};
- } catch (e) {
+ } catch (e: any) {
console.log(e);
return e;
}
@@ -272,7 +274,7 @@ export namespace DashUploadUtils {
* @param suffix If the file doesn't have a suffix and you want to provide it one
* to appear in the new location
*/
- export async function MoveParsedFile(file: File, destination: Directory, suffix: string | undefined = undefined, text?: string): Promise<Upload.FileResponse> {
+ export async function MoveParsedFile(file: formidable.File, destination: Directory, suffix: string | undefined = undefined, text?: string): Promise<Upload.FileResponse> {
const { path: sourcePath } = file;
let name = path.basename(sourcePath);
suffix && (name += suffix);
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 78b75d6be..aa9bfcfa7 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -124,7 +124,7 @@ export default class RouteManager {
}
await tryExecute(secureHandler, { ...core, user });
} else {
- req.session!.target = target;
+ //req.session!.target = target;
if (publicHandler) {
await tryExecute(publicHandler, core);
if (!res.headersSent) {
diff --git a/src/server/Search.ts b/src/server/Search.ts
index 68f61deb2..25bd8badf 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -49,7 +49,7 @@ export namespace Search {
},
json: true
});
- } catch (e) {
+ } catch (e: any) {
console.log(red("Unable to clear search..."));
console.log(red(e.message));
}
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 64bafe7fb..4453b83bf 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -128,7 +128,7 @@ export namespace GoogleApiServerUtils {
* @param userId the id of the Dash user making the request to the API
* @returns the relevant 'googleapis' wrapper, if any
*/
- export async function GetEndpoint(sector: string, userId: string): Promise<Opt<Endpoint>> {
+ export async function GetEndpoint(sector: string, userId: string): Promise<Endpoint | void> {
return new Promise(async resolve => {
const auth = await retrieveOAuthClient(userId);
if (!auth) {
@@ -157,7 +157,7 @@ export namespace GoogleApiServerUtils {
* npm-installed API wrappers that use authenticated client instances rather than access codes for
* security.
*/
- export async function retrieveOAuthClient(userId: string): Promise<OAuth2Client> {
+ export async function retrieveOAuthClient(userId: string): Promise<OAuth2Client | void> {
return new Promise(async resolve => {
const { credentials, refreshed } = await retrieveCredentials(userId);
if (!credentials) {
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 3fbd4b3a7..b736f0d35 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -55,7 +55,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
const user = new User(model);
- User.findOne({ email }, (err, existingUser) => {
+ User.findOne({ email }, (err: any, existingUser: any) => {
if (err) { return next(err); }
if (existingUser) {
return res.redirect("/login");
@@ -72,9 +72,8 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
};
const tryRedirectToTarget = (req: Request, res: Response) => {
- if (req.session && req.session.target) {
- const target = req.session.target;
- req.session.target = undefined;
+ const target = (req.session as any)?.target;
+ if (req.session && target) {
res.redirect(target);
} else {
res.redirect("/home");
@@ -88,7 +87,7 @@ const tryRedirectToTarget = (req: Request, res: Response) => {
*/
export let getLogin = (req: Request, res: Response) => {
if (req.user) {
- req.session!.target = undefined;
+ //req.session.target = undefined;
return res.redirect("/home");
}
res.render("login.pug", {
@@ -160,7 +159,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
});
},
function (token: string, done: any) {
- User.findOne({ email }, function (err, user: DashUserModel) {
+ User.findOne({ email }, function (err: any, user: DashUserModel) {
if (!user) {
// NO ACCOUNT WITH SUBMITTED EMAIL
res.redirect("/forgotPassword");
@@ -202,7 +201,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
};
export let getReset = function (req: Request, res: Response) {
- User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
+ User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
if (!user || err) {
return res.redirect("/forgotPassword");
}
@@ -216,7 +215,7 @@ export let getReset = function (req: Request, res: Response) {
export let postReset = function (req: Request, res: Response) {
async.waterfall([
function (done: any) {
- User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
+ User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
if (!user || err) {
return res.redirect('back');
}
diff --git a/src/server/authentication/Passport.ts b/src/server/authentication/Passport.ts
index 9b0069414..d7f891c34 100644
--- a/src/server/authentication/Passport.ts
+++ b/src/server/authentication/Passport.ts
@@ -4,12 +4,12 @@ import { default as User } from './DashUserModel';
const LocalStrategy = passportLocal.Strategy;
-passport.serializeUser<any, any>((user, done) => {
- done(undefined, user.id);
+passport.serializeUser<any, any>((req, user, done) => {
+ done(undefined, (user as any)?.id);
});
passport.deserializeUser<any, any>((id, done) => {
- User.findById(id, (err, user) => {
+ User.findById(id, (err: any, user: any) => {
done(err, user);
});
});
diff --git a/src/server/remapUrl.ts b/src/server/remapUrl.ts
index e9f9da25a..b8e17ec66 100644
--- a/src/server/remapUrl.ts
+++ b/src/server/remapUrl.ts
@@ -47,7 +47,7 @@ async function update() {
await cursor.forEach(updateDoc);
await Promise.all(updates.map(doc => {
console.log(doc[0], doc[1]);
- return new Promise(res => Database.Instance.update(doc[0], doc[1], () => {
+ return new Promise<void>(res => Database.Instance.update(doc[0], doc[1], () => {
console.log("wrote " + JSON.stringify(doc[1]));
res();
}, false));
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index de93b64c3..24cc3b796 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -31,7 +31,7 @@ const compiler = webpack(config);
/* 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 disconnect: Function;
export let resolvedPorts: { server: number, socket: number } = { server: 1050, socket: 4321 };
export let resolvedServerUrl: string;
@@ -40,6 +40,14 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
const isRelease = determineEnvironment();
const app = buildWithMiddleware(express());
+ const compiler = webpack(config);
+
+ app.use(require("webpack-dev-middleware")(compiler, {
+ publicPath: config.output.publicPath
+ }));
+
+ app.use(require("webpack-hot-middleware")(compiler));
+
// route table managed by express. routes are tested sequentially against each of these map rules. when a match is found, the handler is called to process the request
app.get(new RegExp(/^\/+$/), (req, res) => res.redirect(req.user ? "/home" : "/login")); // target urls that consist of one or more '/'s with nothing in between
app.use(express.static(publicDirectory, { setHeaders: res => res.setHeader("Access-Control-Allow-Origin", "*") })); //all urls that start with dash's public directory: /files/ (e.g., /files/images, /files/audio, etc)
@@ -66,7 +74,7 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
// 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));
+ //disconnect = async () => new Promise<Error>(resolve => server.close(resolve));
return isRelease;
}
@@ -92,6 +100,13 @@ function buildWithMiddleware(server: express.Express) {
passport.session(),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
res.locals.user = req.user;
+ if ((req.originalUrl.endsWith(".png") /*|| req.originalUrl.endsWith(".js")*/) && req.method === 'GET' && (res as any)._contentLength) {
+ const period = 30000;
+ res.set('Cache-control', `public, max-age=${period}`);
+ } else {
+ // for the other requests set strict no caching parameters
+ res.set('Cache-control', `no-store`);
+ }
next();
}
].forEach(next => server.use(next));
@@ -178,11 +193,21 @@ function proxyServe(req: any, requrl: string, response: any) {
return `href="${resolvedServerUrl + "/corsProxy/http" + href}"`;
};
const zipToStringDecoder = new (require('string_decoder').StringDecoder)('utf8');
- const htmlText = zipToStringDecoder.write(zlib.gunzipSync(htmlBodyMemoryStream.read()).toString('utf8')
- .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
- .replace(/href="http([^"]*)"/g, replacer)
- .replace(/target="_blank"/g, ""));
- rewrittenHtmlBody = zlib.gzipSync(htmlText);
+ // const htmlText = zipToStringDecoder.write(zlib.gunzipSync(htmlBodyMemoryStream.read()).toString('utf8')
+ // .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
+ // .replace(/href="http([^"]*)"/g, replacer)
+ // .replace(/target="_blank"/g, ""));
+ // rewrittenHtmlBody = zlib.gzipSync(htmlText);
+ const bodyStream = htmlBodyMemoryStream.read();
+ if (bodyStream) {
+ const htmlText = zipToStringDecoder.write(zlib.gunzipSync(bodyStream).toString('utf8')
+ .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
+ // .replace(/href="http([^"]*)"/g, replacer)
+ .replace(/target="_blank"/g, ""));
+ rewrittenHtmlBody = zlib.gzipSync(htmlText);
+ } else {
+ console.log("EMPTY body: href");
+ }
} catch (e) { console.log(e); }
}
});