aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-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.ts92
-rw-r--r--src/client/util/CurrentUserUtils.ts247
-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.tsx5
-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.tsx23
-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.tsx10
-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.tsx4
-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.tsx30
-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
91 files changed, 1750 insertions, 1633 deletions
diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
new file mode 100644
index 000000000..1d4653471
--- /dev/null
+++ b/src/client/ClientRecommender.tsx
@@ -0,0 +1,421 @@
+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 855f48f7e..cda108058 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 { ScriptingGlobals } from "../util/ScriptingGlobals";
+import { Scripting } from "../util/Scripting";
import { MainViewModal } from "../views/MainViewModal";
import "./GoogleAuthenticationManager.scss";
@@ -165,4 +165,4 @@ export class GoogleAuthenticationManager extends React.Component<{}> {
}
-ScriptingGlobals.add("GoogleAuthenticationManager", GoogleAuthenticationManager); \ No newline at end of file
+Scripting.addGlobal("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 2b2931a97..80961af14 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -6,6 +6,7 @@ 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>;
@@ -378,7 +379,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 9bcd23aa0..df573a377 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 { ScriptingGlobals } from "../util/ScriptingGlobals";
+import { Scripting } from "../util/Scripting";
import { undoBatch, UndoManager } from "../util/UndoManager";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
@@ -61,6 +61,7 @@ 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 {
@@ -179,7 +180,7 @@ export class DocumentOptions {
layout?: string | Doc; // default layout string for a document
contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
childLimitHeight?: number; // whether to limit the height of collection children. 0 - means height can be no bigger than width
- childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view)
+ childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
childLayoutString?: string; // template string for collection to use to render its children
childDontRegisterViews?: boolean;
childHideLinkButton?: boolean; // hide link buttons on all children
@@ -234,7 +235,7 @@ export class DocumentOptions {
docColorBtn?: string;
userColorBtn?: string;
canClick?: string;
- script?: ScriptField;
+ script?: string;
numBtnType?: string;
numBtnMax?: number;
numBtnMin?: number;
@@ -360,34 +361,31 @@ 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: "@links(self)"
- }
+ options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.SEARCH, {
layout: { view: SearchBox, dataField: defaultDataKey },
- options: { _width: 400, links: "@links(self)" }
+ options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.FILTER, {
layout: { view: FilterBox, dataField: defaultDataKey },
- options: { _width: 400, links: "@links(self)" }
+ options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.COLOR, {
layout: { view: ColorBox, dataField: defaultDataKey },
- options: { _nativeWidth: 220, _nativeHeight: 300, links: "@links(self)" }
+ options: { _nativeWidth: 220, _nativeHeight: 300, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.IMG, {
layout: { view: ImageBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.WEB, {
layout: { view: WebBox, dataField: defaultDataKey },
- options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
+ options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.COL, {
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: "@links(self)" }
+ options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.KVP, {
layout: { view: KeyValueBox, dataField: defaultDataKey },
@@ -395,19 +393,19 @@ export namespace Docs {
}],
[DocumentType.VID, {
layout: { view: VideoBox, dataField: defaultDataKey },
- options: { _currentTimecode: 0, links: "@links(self)" },
+ options: { _currentTimecode: 0, links: ComputedField.MakeFunction("links(self)") as any },
}],
[DocumentType.AUDIO, {
layout: { view: AudioBox, dataField: defaultDataKey },
- options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" }
+ options: { _height: 100, backgroundColor: "lightGray", links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.PDF, {
layout: { view: PDFBox, dataField: defaultDataKey },
- options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
+ options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.MAP, {
layout: { view: MapBox, dataField: defaultDataKey },
- options: { _height: 600, _width: 800, links: "@links(self)" }
+ options: { _height: 600, _width: 800, links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.IMPORT, {
layout: { view: DirectoryImportBox, dataField: defaultDataKey },
@@ -418,7 +416,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: "@links(self)",
+ links: ComputedField.MakeFunction("links(self)") as any,
_removeDropProperties: new List(["_layerTags", "isLinkButton"]),
}
}],
@@ -434,61 +432,61 @@ export namespace Docs {
}],
[DocumentType.SCRIPTING, {
layout: { view: ScriptingBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.YOUTUBE, {
layout: { view: YoutubeBox, dataField: defaultDataKey }
}],
[DocumentType.LABEL, {
layout: { view: LabelBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.EQUATION, {
layout: { view: EquationBox, dataField: defaultDataKey },
- options: { links: "@links(self)", hideResizeHandles: true, hideDecorationTitle: true }
+ options: { links: ComputedField.MakeFunction("links(self)") as any, hideResizeHandles: true, hideDecorationTitle: true }
}],
[DocumentType.FUNCPLOT, {
layout: { view: FunctionPlotBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.BUTTON, {
layout: { view: LabelBox, dataField: "onClick" },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.SLIDER, {
layout: { view: SliderBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.PRES, {
layout: { view: PresBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.FONTICON, {
layout: { view: FontIconBox, dataField: defaultDataKey },
- options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" },
+ options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: ComputedField.MakeFunction("links(self)") as any },
}],
[DocumentType.WEBCAM, {
layout: { view: DashWebRTCVideo, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.PRESELEMENT, {
layout: { view: PresElementBox, dataField: defaultDataKey }
}],
[DocumentType.MARKER, {
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { links: "@links(self)", hideLinkButton: true }
+ options: { links: ComputedField.MakeFunction("links(self)") as any, 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: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.SCREENSHOT, {
layout: { view: ScreenshotBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.COMPARISON, {
layout: { view: ComparisonBox, dataField: defaultDataKey },
- options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" }
+ options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: ComputedField.MakeFunction("links(self)") as any }
}],
[DocumentType.GROUPDB, {
data: new List<Doc>(),
@@ -497,7 +495,7 @@ export namespace Docs {
}],
[DocumentType.GROUP, {
layout: { view: EmptyBox, dataField: defaultDataKey },
- options: { links: "@links(self)" }
+ options: { links: ComputedField.MakeFunction("links(self)") as any }
}],
]);
@@ -594,11 +592,6 @@ 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);
}
}
@@ -662,7 +655,7 @@ export namespace Docs {
export function ImageDocument(url: string, options: DocumentOptions = {}) {
const imgField = new ImageField(url);
- return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
+ return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
}
export function PresDocument(initial: List<Doc> = new List(), options: DocumentOptions = {}) {
@@ -765,7 +758,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 = "@links(self)";
+ I.links = ComputedField.MakeFunction("links(self)") as any;
I[Initializing] = false;
return I;
}
@@ -1450,13 +1443,13 @@ export namespace DocUtils {
if (doc) {
const proto = Doc.GetProto(doc);
proto.text = result.rawText;
- proto.fileUpload = pathname.replace(/.*\//, "").replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+ proto.fileUpload = basename(pathname).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 ?? ((StrCast((result.exifData?.data as any)?.Orientation).includes("Rotate 90")) ? 5 : undefined);
+ proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation;
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 (NumCast(proto["data-nativeOrientation"]) >= 5) {
+ if (Number(result.exifData?.data?.image?.Orientation) >= 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);
}
@@ -1479,28 +1472,27 @@ 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)) {
- name && type && processFileupload(generatedDocuments, name, type, result, options);
+ processFileupload(generatedDocuments, name, type, result, options);
}
return generatedDocuments;
}
export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
const generatedDocuments: Doc[] = [];
- const upfiles = await Networking.UploadFilesToServer(files);
- for (const { source: { name, type }, result } of upfiles) {
- name && type && processFileupload(generatedDocuments, name, type, result, options);
+ for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
+ processFileupload(generatedDocuments, name, type, result, options);
}
return generatedDocuments;
}
}
-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) {
+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) {
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}`;
});
-ScriptingGlobals.add(function openTabAlias(tab: Doc) {
+Scripting.addGlobal(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 f02c5a5a1..a8b0da369 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -7,6 +7,7 @@ import { List } from "../../fields/List";
import { PrefetchProxy } from "../../fields/Proxy";
import { RichTextField } from "../../fields/RichTextField";
import { listSpec } from "../../fields/Schema";
+import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
import { ComputedField, ScriptField } from "../../fields/ScriptField";
import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
import { nullAudio } from "../../fields/URLField";
@@ -15,7 +16,9 @@ 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";
@@ -29,7 +32,7 @@ import { DragManager } from "./DragManager";
import { makeTemplate } from "./DropConverter";
import { HistoryUtil } from "./History";
import { LinkManager } from "./LinkManager";
-import { ScriptingGlobals } from "./ScriptingGlobals";
+import { Scripting } from "./Scripting";
import { SearchUtil } from "./SearchUtil";
import { SelectionManager } from "./SelectionManager";
import { ColorScheme } from "./SettingsManager";
@@ -48,6 +51,7 @@ interface Button {
numBtnMax?: number;
switchToggle?: boolean;
script?: string;
+ checkResult?: string;
width?: number;
list?: string[];
ignoreClick?: boolean;
@@ -158,37 +162,90 @@ 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;
+
+ const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)");
+ const carousel = CarouselDocument([], {
+ title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10,
+ onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true
+ });
+
+ const details = TextDocument("", { title: "details", _height: 200, _autoHeight: true, system: true });
+ const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 75, _autoHeight: true, system: true });
+ const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 150, _autoHeight: true, system: true });
+
+ const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"];
+ const detailedTemplate = {
+ doc: {
+ type: "doc", content: buxtonFieldKeys.map(fieldKey => ({
+ type: "paragraph",
+ content: [{ type: "dashField", attrs: { fieldKey } }]
+ }))
+ },
+ selection: { type: "text", anchor: 1, head: 1 },
+ storedMarks: []
+ };
+ details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" "));
+
+ const shared = { _autoHeight: true, _xMargin: 0 };
+ const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" };
+ const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true };
+
+ const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
+ descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
+ new SchemaHeaderField("[A Short Description]", "dimgray", undefined, undefined, undefined, false),
+ new SchemaHeaderField("[Long Description]", "dimgray", undefined, undefined, undefined, true),
+ new SchemaHeaderField("[Details]", "dimgray", undefined, undefined, undefined, true),
+ ]);
+ const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts, _chromeHidden: true, system: true });
+ detailView.isTemplateDoc = makeTemplate(detailView);
+
+ details.title = "Details";
+ short.title = "A Short Description";
+ long.title = "Long Description";
+
+ doc["template-button-detail"] = CurrentUserUtils.createToolButton({
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
+ dragFactory: new PrefetchProxy(detailView) as any as Doc,
+ title: "detailView",
+ icon: "window-maximize",
+ system: true,
+ btnType: ButtonType.ToolButton,
+ });
+ }
const requiredTypes = [
doc["template-button-slides"] as Doc,
@@ -481,11 +538,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 = Cast(doc.myItemCreators, Doc, null);
+ const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null);
if (dragCreatorSet) {
- const dragCreators = Cast(dragCreatorSet.data, listSpec(Doc));
+ const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc));
if (dragCreators) {
- const dragDocs = await Promise.all(Array.from(dragCreators));
+ const dragDocs = await Promise.all(dragCreators);
alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
}
}
@@ -678,16 +735,12 @@ 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
}));
}
@@ -934,37 +987,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(value, _readOnly_)'
+ script: 'setFont'
},
- { 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: "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: "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", _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_)' },
+ { 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)' },
];
return tools;
}
static inkTools(doc: Doc) {
const tools: Button[] = [
- { 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_)' },
+ { 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' },
];
return tools;
}
@@ -978,7 +1031,8 @@ export class CurrentUserUtils {
btnType: ButtonType.ToggleButton,
buttonText: "Show Preview",
icon: "eye",
- click: 'toggleSchemaPreview(_readOnly_)',
+ click: 'toggleSchemaPreview()',
+ checkResult: 'toggleSchemaPreview(true)'
},
];
return tools;
@@ -987,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(_readOnly_)' },
- { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' },
+ { 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: "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(value, _readOnly_)' },
+ { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL' },
];
return tools;
@@ -1005,17 +1059,17 @@ export class CurrentUserUtils {
CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
CollectionViewType.Grid],
- script: 'setView(value, _readOnly_)',
+ script: 'setView',
}, // Always show
{
title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
- script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()'
+ script: "setBackgroundColor", hidden: 'selectedDocumentType()'
}, // Only when a document is selected
{
title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
- script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
+ script: "setHeaderColor", hidden: 'selectedDocumentType()',
}, // Only when a document is selected
- { 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: "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: "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
@@ -1029,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 }) => {
+ (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded, checkResult }) => {
const menuDocList: Doc[] = [];
if (subMenu) {
// default is textTools
@@ -1051,8 +1105,7 @@ export class CurrentUserUtils {
tools = CurrentUserUtils.textTools(doc);
break;
}
- tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => {
- const computed = click ? ComputedField.MakeFunction(click) as any : "transparent";
+ tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle, checkResult }) => {
menuDocList.push(Docs.Create.FontIconDocument({
_nativeWidth: width ? width : 25,
_nativeHeight: 25,
@@ -1063,7 +1116,7 @@ export class CurrentUserUtils {
numBtnType,
numBtnMin,
numBtnMax,
- script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
+ script,
btnType: btnType,
btnList: new List<string>(list),
ignoreClick: ignoreClick,
@@ -1075,10 +1128,10 @@ export class CurrentUserUtils {
title,
switchToggle,
color: Colors.WHITE,
- backgroundColor: computed,
+ backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
_dropAction: "alias",
_removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
- onClick: click ? ScriptField.MakeScript(click) : undefined
+ onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined
}));
});
docList.push(CurrentUserUtils.linearButtonList({
@@ -1088,7 +1141,7 @@ export class CurrentUserUtils {
linearViewExpandable: true,
icon: title,
_height: 30,
- // backgroundColor: hidden ? ComputedField.MakeFunction(hidden, { }, { _readOnly_: true }) as any : "transparent",
+ backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
linearViewIsExpanded: expanded ? !(ComputedField.MakeFunction(expanded) as any) : undefined,
hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
}, menuDocList));
@@ -1100,7 +1153,7 @@ export class CurrentUserUtils {
_height: 25,
icon,
toolTip,
- script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
+ script,
btnType,
btnList: new List<string>(list),
ignoreClick,
@@ -1111,11 +1164,11 @@ export class CurrentUserUtils {
dontUndo: true,
title,
color: Colors.WHITE,
- // backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult, {}, {_readOnly_:true}) as any : "transparent",
+ backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) 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" }, { _readOnly_: false }) : undefined
+ onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined
}));
}
});
@@ -1330,7 +1383,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 (await fetch("/resolvedPorts")).text());
+ resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts"));
DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email);
result.cacheDocumentIds && (await DocServer.GetRefFields(result.cacheDocumentIds.split(";")));
return result;
@@ -1513,7 +1566,7 @@ export class CurrentUserUtils {
@computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
}
-ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
+Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
const copy = Doc.copyDragFactory(dragFactory);
if (copy) {
CollectionDockingView.AddSplit(copy, "right");
@@ -1521,27 +1574,27 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
view && SelectionManager.SelectView(view, false);
}
});
-ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); },
+Scripting.addGlobal(function MySharedDocs() { return Doc.SharingDoc(); },
"document containing all shared Docs");
-ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
+Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
"is Dash in novice mode");
-ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
+Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
"creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
+Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
"creates a new dashboard when called");
-ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
+Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
"creates a new presentation when called");
-ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); },
+Scripting.addGlobal(function createNewFolder() { return MainView.Instance.createNewFolder(); },
"creates a new folder in myFiles when called");
-ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
+Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
"returns all the links to the document or its annotations", "(doc: any)");
-ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); },
+Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); },
"imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function shareDashboard(dashboard: Doc) {
+Scripting.addGlobal(function shareDashboard(dashboard: Doc) {
SharingManager.Instance.open(undefined, dashboard);
},
"opens sharing dialog for Dashboard");
-ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
+Scripting.addGlobal(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)!);
@@ -1549,7 +1602,7 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
}
},
"Remove Dashboard from Dashboards");
-ScriptingGlobals.add(async function addToDashboards(dashboard: Doc) {
+Scripting.addGlobal(async function addToDashboards(dashboard: Doc) {
const dashboardAlias = Doc.MakeAlias(dashboard);
const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]);
@@ -1573,7 +1626,7 @@ ScriptingGlobals.add(async function addToDashboards(dashboard: Doc) {
/**
* Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard.
*/
-ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) {
+Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
if (dashboard[DataSym] instanceof Doc) {
const allDocs = DocListCast(dashboard["data-all"]);
const onScreenTab = DocListCast(dashboard.data)[0];
@@ -1585,7 +1638,7 @@ ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) {
}
return [];
});
-ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
+Scripting.addGlobal(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);
@@ -1596,11 +1649,11 @@ ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colTy
else if (selected && !colType && !docType) return false;
else return true;
});
-ScriptingGlobals.add(function makeTopLevelFolder() {
+Scripting.addGlobal(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);
});
-ScriptingGlobals.add(function toggleComicMode() {
+Scripting.addGlobal(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 a6dcda4bc..a93b2f573 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,6 +1,4 @@
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";
@@ -15,7 +13,6 @@ 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.
@@ -105,17 +102,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: any) {
+ 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) {
console.log(e);
if (overlay) {
DictationOverlay.Instance.isListening = false;
@@ -191,7 +188,7 @@ export namespace DictationManager {
current && sessionResults.push(current);
sessionResults.length && resolve(sessionResults.join(inter || interSession));
} else {
- resolve(current || "");
+ resolve(current);
}
current = undefined;
sessionResults = [];
@@ -225,168 +222,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 0a00ab6e0..66b6a1e44 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 { ScriptingGlobals } from './ScriptingGlobals';
+import { Scripting } from './Scripting';
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(ViewAdjustment.doNothing);
+ res();
})
});
} else {
@@ -227,7 +227,7 @@ export class DocumentManager {
willZoom, afterFocus: (didFocus: boolean) =>
new Promise<ViewAdjustment>(res => {
!noSelect && focusAndFinish(didFocus);
- res(ViewAdjustment.doNothing);
+ res();
})
}); // focus on the target in the context
} else if (delay > 1500) {
@@ -252,7 +252,7 @@ export class DocumentManager {
}
}
-ScriptingGlobals.add(function DocFocusOrOpen(doc: any) {
+Scripting.addGlobal(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 c9c499fff..ae3fa3170 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 as any,
+ view: e.view ? e.view : new Window,
nativeEvent: new DragEvent("dashDragAutoScroll"),
currentTarget: target,
target: target,
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 082b6d8bd..32817eefd 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 { ScriptingGlobals } from "./ScriptingGlobals";
+import { Scripting } from "./Scripting";
import { listSpec } from "../../fields/Schema";
//
@@ -81,5 +81,5 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
data.droppedDocuments[i] = dbox;
});
}
-ScriptingGlobals.add(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); },
+Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); },
"converts the dropped data to buttons", "(dragData: any)"); \ No newline at end of file
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index e6f75a7db..cbe36b401 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -103,7 +103,8 @@ export namespace HistoryUtil {
return undefined;
}
const parser = requiredFields[required];
- const value = parse(parser, opts[required]);
+ let value = opts[required];
+ value = parse(parser, value);
if (value !== null && value !== undefined) {
current[required] = value;
}
@@ -113,7 +114,8 @@ export namespace HistoryUtil {
continue;
}
const parser = optionalFields[opt];
- const value = parse(parser, opts[opt]);
+ let value = opts[opt];
+ value = parse(parser, value);
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 39e9251a5..925b74efa 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -1,8 +1,9 @@
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 { extname } from "path";
+import * as path from 'path';
import Measure, { ContentRect } from "react-measure";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
@@ -85,7 +86,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 = extname(file.name).toLowerCase();
+ const ext = path.extname(file.name).toLowerCase();
if (AcceptableMedia.imageFormats.includes(ext)) {
validated.push(file);
}
@@ -119,7 +120,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
}
const { accessPaths, exifData } = result;
const path = Utils.prepend(accessPaths.agnostic.client);
- const document = type && await DocUtils.DocumentFromType(type, path, { _width: 300 });
+ const document = await DocUtils.DocumentFromType(type, path, { _width: 300, title: name });
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 df2c02a8d..62b13e2c6 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(ViewAdjustment.doNothing));
+ return new Promise<ViewAdjustment>(res2 => res2());
}
});
} else {
diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts
index 42a6493ea..94806a7ba 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) {
- ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name));
+ Scripting.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 {
- ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name));
+ Scripting.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 === "(") {
- ScriptingGlobals.add(f, StrCast(scriptDoc.description));
+ Scripting.addGlobal(f, StrCast(scriptDoc.description));
} else {
- ScriptingGlobals.add(f, StrCast(scriptDoc.description), parameters);
+ Scripting.addGlobal(f, StrCast(scriptDoc.description), parameters);
}
}
} \ No newline at end of file
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 3b0a47b54..40b94024e 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,15 +1,16 @@
+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 * as ts from "typescript";
-import { Doc, Field } from "../../fields/Doc";
-import { scriptingGlobals, ScriptingGlobals } from "./ScriptingGlobals";
-export { ts };
-
+import { Doc, Field } from '../../fields/Doc';
export interface ScriptSuccess {
success: true;
@@ -46,6 +47,98 @@ 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);
@@ -180,7 +273,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) {
- ScriptingGlobals.setScriptingGlobals(options.globals);
+ Scripting.setScriptingGlobals(options.globals);
}
const host = new ScriptingCompilerHost;
if (options.traverser) {
@@ -238,9 +331,9 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const result = Run(outputText, paramNames, diagnostics, script, options);
if (options.globals) {
- ScriptingGlobals.resetScriptingGlobals();
+ Scripting.resetScriptingGlobals();
}
return result;
}
-ScriptingGlobals.add(CompileScript);
+Scripting.addGlobal(CompileScript); \ No newline at end of file
diff --git a/src/client/util/ScriptingGlobals.ts b/src/client/util/ScriptingGlobals.ts
deleted file mode 100644
index f151acd81..000000000
--- a/src/client/util/ScriptingGlobals.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-
-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 d1f1a0099..44e44d335 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,4 +1,5 @@
import { observable, action, runInAction } from "mobx";
+import 'source-map-support/register';
import { Without } from "../../Utils";
function getBatchName(target: any, key: string | symbol): string {
@@ -106,7 +107,7 @@ export namespace UndoManager {
}
export function FilterBatches(fieldTypes: string[]) {
const fieldCounts: { [key: string]: number } = {};
- const lastStack = UndoManager.undoStack.slice(-1)[0];//.lastElement();
+ const lastStack = UndoManager.undoStack.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 2e6ea1faa..b9772fd57 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>() {
+export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) {
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() { return this.props.Document; }
+ @computed get Document(): T { return schemaCtor(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>() {
+export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor: (doc: Doc) => T) {
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>() {
+export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
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() { return this.props.Document; }
+ @computed get Document(): T { return schemaCtor(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 Doc.Layout(this.props.Document); }
+ @computed get layoutDoc() { return schemaCtor(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 657d92b8a..cb95a1237 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 { CollectionFreeFormView } from './collections/collectionFreeForm';
+import { InkingStroke } from './InkingStroke';
+import e = require('express');
@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().slice(-1)[0].rootDoc]);
+ }, [SelectionManager.Views().lastElement().rootDoc]);
return true;
}, emptyFunction, this.onMaximizeClick, false, false);
}
@@ -233,13 +233,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
this._snapX = e.pageX;
this._snapY = e.pageY;
- 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));
+ SelectionManager.Views().forEach(docView => this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) }));
}
onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
@@ -401,10 +395,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);
});
@@ -430,7 +424,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
render() {
const bounds = this.Bounds;
- const seldoc = SelectionManager.Views().slice(-1)[0];
+ const seldoc = SelectionManager.Views().lastElement();
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 50dca0a99..04abdbf37 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 { ScriptingGlobals } from "../util/ScriptingGlobals";
+import { Scripting } from "../util/Scripting";
import { SelectionManager } from "../util/SelectionManager";
import { Transform } from "../util/Transform";
import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
@@ -943,11 +943,11 @@ export enum ToolglassTools {
None = "none",
}
-ScriptingGlobals.add("GestureOverlay", GestureOverlay);
-ScriptingGlobals.add(function setToolglass(tool: any) {
+Scripting.addGlobal("GestureOverlay", GestureOverlay);
+Scripting.addGlobal(function setToolglass(tool: any) {
runInAction(() => GestureOverlay.Instance.Tool = tool);
});
-ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
+Scripting.addGlobal(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 @@ ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowSta
SetActiveDash(dash);
});
});
-ScriptingGlobals.add(function resetPen() {
+Scripting.addGlobal(function resetPen() {
runInAction(() => {
SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? "2");
});
}, "resets the pen tool");
-ScriptingGlobals.add(function createText(text: any, x: any, y: any) {
+Scripting.addGlobal(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 a7f511ab4..24f796105 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -51,7 +51,6 @@ 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 40fe6aa68..9dbd97c16 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -24,7 +24,9 @@ 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";
@@ -44,8 +46,11 @@ 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>() {
+export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) {
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 8560ccb29..7553c8118 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,15 +1,13 @@
-// if ((module as any).hot) {
-// (module as any).hot.accept();
-// }
-
-import * as React from 'react';
-import * as ReactDOM from 'react-dom';
-import { AssignAllExtensions } from "../../extensions/General/Extensions";
+import { MainView } from "./MainView";
import { Docs } from "../documents/Documents";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { LinkManager } from "../util/LinkManager";
+import * as ReactDOM from 'react-dom';
+import * as React from 'react';
+import { DocServer } from "../DocServer";
+import { AssignAllExtensions } from "../../extensions/General/Extensions";
+import { Networking } from "../Network";
import { CollectionView } from "./collections/CollectionView";
-import { MainView } from "./MainView";
+import { LinkManager } from "../util/LinkManager";
AssignAllExtensions();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8c0795881..fc1c694a2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,6 +5,7 @@ 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';
@@ -23,7 +24,7 @@ import { DocumentManager } from '../util/DocumentManager';
import { GroupManager } from '../util/GroupManager';
import { HistoryUtil } from '../util/History';
import { Hypothesis } from '../util/HypothesisUtils';
-import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { Scripting } from '../util/Scripting';
import { SelectionManager } from '../util/SelectionManager';
import { ColorScheme, SettingsManager } from '../util/SettingsManager';
import { SharingManager } from '../util/SharingManager';
@@ -44,6 +45,7 @@ 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";
@@ -163,28 +165,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.faSearchPlus, fa.faVolumeUp, fa.faVolumeMute, fa.faMapMarkerAlt);
this.initAuthenticationRouters();
}
@@ -694,4 +696,4 @@ export class MainView extends React.Component {
}
}
-ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); \ No newline at end of file
+Scripting.addGlobal(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 0f51cf9b2..7cf388872 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 } from "../../Utils";
-import { DocUtils } from "../documents/Documents";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils, emptyPath } from "../../Utils";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { DragManager } from "../util/DragManager";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
+import { Scripting } from "../util/Scripting";
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
-ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) {
+Scripting.addGlobal(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 dd6448654..f9dab9f82 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -18,10 +18,6 @@ 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;
@@ -96,20 +92,6 @@ 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);
}
@@ -240,7 +222,6 @@ 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 47a5cd07e..f53944805 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -33,7 +33,6 @@ import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
import "./PropertiesView.scss";
import { DefaultStyleProvider } from "./StyleProvider";
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;
@@ -1302,7 +1301,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={this.toggleAnchor} onClick={e => e.stopPropagation()}
className="propertiesButton"
>
- <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+ <FontAwesomeIcon className="fa-icon" icon={faAnchor} size="lg" />
</button>
</div>
<div className="propertiesView-input inline" id="propertiesView-displayArrow">
@@ -1312,7 +1311,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
onPointerDown={this.toggleArrow} onClick={e => e.stopPropagation()}
className="propertiesButton"
>
- <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+ <FontAwesomeIcon className="fa-icon" icon={faArrowRight} size="lg" />
</button>
</div>
</div>
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index b7ea124b9..e2b5d8dc3 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -1,17 +1,17 @@
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
import * as React from "react";
-import { Doc, Opt } from "../../fields/Doc";
-import { ScriptField } from "../../fields/ScriptField";
-import { ScriptCast } from "../../fields/Types";
+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 { DragManager } from "../util/DragManager";
+import { ScriptCast } from "../../fields/Types";
import { CompileScript } from "../util/Scripting";
+import { ScriptField } from "../../fields/ScriptField";
+import { DragManager } from "../util/DragManager";
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 4ed6da24a..c598b2861 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -3,8 +3,7 @@ import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { DocumentManager } from '../util/DocumentManager';
-import { CompileScript, Transformer, ts } from '../util/Scripting';
-import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { CompileScript, Scripting, Transformer, ts } from '../util/Scripting';
import { DocumentIconContainer } from './nodes/DocumentIcon';
import { OverlayView } from './OverlayView';
import './ScriptingRepl.scss';
@@ -82,7 +81,7 @@ export class ScriptingRepl extends React.Component {
transformer: context => {
const knownVars: { [name: string]: number } = {};
const usedDocuments: number[] = [];
- ScriptingGlobals.getGlobals().forEach((global: any) => knownVars[global] = 1);
+ Scripting.getGlobals().forEach(global => knownVars[global] = 1);
return root => {
function visit(node: ts.Node) {
let skip = false;
@@ -125,7 +124,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 = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
+ const globals = Scripting.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 2782574c5..ce363cc47 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 { DashColor, lightOrDark } from '../../Utils';
+import { lightOrDark, DashColor } 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 b3a24e031..ff3f92364 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, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
+import { Scripting } from "../util/Scripting";
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> {
}
}
-ScriptingGlobals.add(function switchView(doc: Doc, template: Doc | undefined) {
+Scripting.addGlobal(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 92d3e2bed..82b0218bf 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -1,17 +1,18 @@
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
import * as React from "react";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { List } from "../../../fields/List";
-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 "../global/globalCssVariables.scss";
import "./Keyframe.scss";
import "./Timeline.scss";
+import "../global/globalCssVariables.scss";
+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 { List } from "../../../fields/List";
+import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../fields/Schema";
+import { Transform } from "../../util/Transform";
import { TimelineMenu } from "./TimelineMenu";
+import { Docs } from "../../documents/Documents";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { emptyPath } from "../../../Utils";
/**
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index e80ba6f36..c7e62c15d 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -13,7 +13,6 @@ 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
@@ -347,9 +346,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 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 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>
<div className="grid-box overview-tool">
<div className="overview-box">
@@ -507,7 +506,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 as IconLookup} />
+ <FontAwesomeIcon className="resize" icon={faGripLines} />
</div>
</div>
</div>
diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx
index aa422c092..53ca9acad 100644
--- a/src/client/views/animationtimeline/TimelineMenu.tsx
+++ b/src/client/views/animationtimeline/TimelineMenu.tsx
@@ -5,7 +5,6 @@ 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
@@ -42,7 +41,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 as IconLookup} 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} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => {
e.stopPropagation();
text = e.target.value;
}} onKeyDown={(e) => {
@@ -53,7 +52,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 as IconLookup} size="lg" /><p className="timeline-menu-desc" onClick={(e) => {
+ this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine} 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 01f41869e..3c66faf0c 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -3,17 +3,23 @@ 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() {
+export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocument) {
@computed get scrollSpeed() {
return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
}
@@ -22,7 +28,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
componentWillUnmount() { this._dropDisposer?.(); }
- protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //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 467c2893f..6c2c27e8e 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -3,6 +3,8 @@ 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';
@@ -10,15 +12,18 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { StyleProp } from '../StyleProvider';
import "./CollectionCarouselView.scss";
-import { CollectionSubView } from './CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+
+type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
+const CarouselDocument = makeInterface(documentSchema, collectionSchema);
@observer
-export class CollectionCarouselView extends CollectionSubView() {
+export class CollectionCarouselView extends CollectionSubView(CarouselDocument) {
private _dropDisposer?: DragManager.DragDropDisposer;
componentWillUnmount() { this._dropDisposer?.(); }
- protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //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 9e8374605..f543d924d 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 { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, DocListCastAsync, DataSym } 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 { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { Scripting } from '../../util/Scripting';
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() {
+export class CollectionDockingView extends CollectionSubView(doc => doc) {
@observable public static Instance: CollectionDockingView;
public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) {
return {
@@ -468,8 +468,8 @@ export class CollectionDockingView extends CollectionSubView() {
}
}
-ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); },
+Scripting.addGlobal(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); },
"opens up document in a lightbox", "(doc: any)");
-ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
+Scripting.addGlobal(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
"opens up document in tab on right side of the screen", "(doc: any)");
-ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); \ No newline at end of file
+Scripting.addGlobal(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 364a2440e..131f5ba46 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 { ScriptingGlobals } from "../../util/ScriptingGlobals";
+import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -1285,7 +1285,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
);
}
}
-ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
+Scripting.addGlobal(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 0a336c544..bc1407c53 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() {
+export class CollectionPileView extends CollectionSubView(doc => doc) {
_originalChrome: any = "";
_disposers: { [name: string]: IReactionDisposer } = {};
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 61ddbe70d..7d9dc39ae 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -11,7 +11,7 @@ import { computedFn } from "mobx-utils";
import { Doc, DocListCast } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
+import { listSpec, makeInterface } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
import {
@@ -25,7 +25,7 @@ import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
import { LinkManager } from "../../util/LinkManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
+import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
@@ -43,6 +43,8 @@ import {
import { LabelBox } from "../nodes/LabelBox";
import "./CollectionStackedTimeline.scss";
+type PanZoomDocument = makeInterface<[]>;
+const PanZoomDocument = makeInterface();
export type CollectionStackedTimelineProps = {
Play: () => void;
Pause: () => void;
@@ -65,8 +67,11 @@ export enum TrimScope {
}
@observer
-export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
- @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
+export class CollectionStackedTimeline extends CollectionSubView<
+ PanZoomDocument,
+ CollectionStackedTimelineProps
+>(PanZoomDocument) {
+ @observable static SelectingRegion: CollectionStackedTimeline | undefined;
@observable public static CurrentlyPlaying: Doc[];
static RangeScript: ScriptField;
@@ -928,9 +933,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
<div
key="right"
className="collectionStackedTimeline-resizer"
- onPointerDown={(e) =>
- this.onAnchorDown(e, this.props.mark, false)
- }
+ onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, false)}
/>
</>
)}
@@ -938,12 +941,12 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
);
}
}
-ScriptingGlobals.add(function formatToTime(time: number): any {
+Scripting.addGlobal(function formatToTime(time: number): any {
return formatTime(time);
});
-ScriptingGlobals.add(function min(num1: number, num2: number): number {
+Scripting.addGlobal(function min(num1: number, num2: number): number {
return Math.min(num1, num2);
});
-ScriptingGlobals.add(function max(num1: number, num2: number): number {
+Scripting.addGlobal(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 8634ea139..cdc680a08 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,15 +3,16 @@ 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 } from "../../../fields/Doc";
+import { DataSym, Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../fields/Doc";
+import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
+import { listSpec, makeInterface } from "../../../fields/Schema";
import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils, returnTrue, returnEmptyDoclist, returnEmptyFilter } from "../../../Utils";
+import { DocUtils, Docs } from "../../documents/Documents";
import { DragManager, dropActionType } from "../../util/DragManager";
import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
@@ -22,14 +23,18 @@ import { EditableView } from "../EditableView";
import { LightboxView } from "../LightboxView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp } from "../StyleProvider";
+import { StyleProp, DefaultStyleProvider } 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;
@@ -39,7 +44,7 @@ export type collectionStackingViewProps = {
};
@observer
-export class CollectionStackingView extends CollectionSubView<Partial<collectionStackingViewProps>>() {
+export class CollectionStackingView extends CollectionSubView<StackingDocument, Partial<collectionStackingViewProps>>(StackingDocument) {
_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 c025e94a8..c5c3f96e8 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() {
+export class CollectionStaffView extends CollectionSubView(doc => doc) {
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 42e157396..34209ebc9 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 ReactLoading from 'react-loading';
import * as rp from 'request-promise';
-import { Networking } from "../../Network";
+import ReactLoading from 'react-loading';
export interface SubCollectionViewProps extends CollectionViewProps {
@@ -25,15 +25,15 @@ export interface SubCollectionViewProps extends CollectionViewProps {
isAnyChildContentActive: () => boolean;
}
-export function CollectionSubView<X>(moreProps?: X) {
- class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() {
+export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
+ class CollectionSubView extends DocComponent<X & SubCollectionViewProps, T>(schemaCtor) {
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 | null) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this.dropDisposer?.();
this.gestureDisposer?.();
this._multiTouchDisposer?.();
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index d6398fda5..292dfd77c 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, Opt, StrListCast } from "../../../fields/Doc";
+import { Doc, DocCastAsync, 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, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, returnEmptyString } from "../../../Utils";
+import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
+import { Scripting } from "../../util/Scripting";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { EditableView } from "../EditableView";
@@ -20,10 +20,13 @@ 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() {
+export class CollectionTimeView extends CollectionSubView(doc => doc) {
_changing = false;
@observable _layoutEngine = "pivot";
@observable _collapsed: boolean = false;
@@ -233,7 +236,7 @@ export class CollectionTimeView extends CollectionSubView() {
}
}
-ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
+Scripting.addGlobal(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 e84517f40..ea077ea40 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 { listSpec } from '../../../fields/Schema';
+import { Document, 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, returnOne, returnTrue } from '../../../Utils';
+import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnOne } 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<Partial<collectionTreeViewProps>>() {
+export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) {
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 ee2c28b5f..681a15e3d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,14 +1,15 @@
-import { computed, observable, runInAction } from 'mobx';
+import { computed, observable, runInAction, action } 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 } from '../../../fields/Doc';
+import { Doc, DocListCast, StrListCast } from '../../../fields/Doc';
+import { documentSchema } from '../../../fields/documentSchemas';
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';
@@ -32,7 +33,10 @@ 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');
@@ -80,8 +84,11 @@ 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>() {
+export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument) {
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 e2ea81392..aeda71d01 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,12 +4,13 @@ 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 } from "../../../../fields/Schema";
+import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema";
import { ScriptField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
import { TraceMobx } from "../../../../fields/util";
@@ -40,6 +41,7 @@ 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";
@@ -65,6 +67,8 @@ 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;
@@ -77,7 +81,7 @@ export type collectionFreeformViewProps = {
};
@observer
-export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
+export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, Partial<collectionFreeformViewProps>>(PanZoomDocument) {
public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
private _lastNudge: any;
@@ -91,7 +95,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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();
@@ -106,7 +109,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@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;
@@ -161,7 +163,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.layoutDoc._panY = vals.bounds.cy;
this.layoutDoc._viewScale = vals.scale;
}
- freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
+ freeformData = (force?: boolean) => 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);
@@ -203,7 +205,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
- CollectionFreeFormDocumentView.setupKeyframes(newBoxes, NumCast(this.Document._currentFrame), true);
+ CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document._currentFrame, true);
}
}
return retVal;
@@ -233,12 +235,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
if (this.Document._currentFrame !== undefined) {
- CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
+ CollectionFreeFormDocumentView.setupKeyframes([d], 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(NumCast(this.Document._currentFrame), d, vals);
+ CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, vals);
} else {
d.x = x + NumCast(d.x) - dropPos[0];
d.y = y + NumCast(d.y) - dropPos[1];
@@ -633,7 +635,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@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(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true);
+ this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
}
@@ -965,10 +967,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
.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 }
@@ -1043,11 +1045,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) };
+ state.initializers![this.Document[Id]] = { panX: this.Document._panX, panY: this.Document._panY };
HistoryUtil.pushState(state);
} else if (init.panX !== this.Document._panX || init.panY !== this.Document._panY) {
- init.panX = NumCast(this.Document._panX);
- init.panY = NumCast(this.Document._panY);
+ init.panX = this.Document._panX;
+ init.panY = this.Document._panY;
HistoryUtil.pushState(state);
}
}
@@ -1142,7 +1144,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
isContentActive = () => this.props.isSelected() || this.props.isContentActive();
- getChildDocView(entry: PoolData, renderIndex: number) {
+ getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
@@ -1151,8 +1153,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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<Partial<collection
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, NumCast(this.Document._currentFrame));
+ CollectionFreeFormDocumentView.getValues(params.pair.layout, 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,12 +1256,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
-
- 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 || ""));
}
@@ -1306,7 +1300,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
- @observable _numLoaded = 1;
get doLayoutComputation() {
const { newPool, computedElementData } = this.doInternalLayoutComputation;
const array = Array.from(newPool.entries());
@@ -1325,9 +1318,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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, i) =>
+ Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry =>
elements.push({
- ele: this.getChildDocView(entry[1], i),
+ ele: this.getChildDocView(entry[1]),
bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
}));
@@ -1376,38 +1369,35 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
componentDidMount() {
super.componentDidMount?.();
this.props.setContentView?.(this);
- 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));
+ 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;
}
- 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 });
- }));
+ }, { fireImmediately: true });
}
componentWillUnmount() {
@@ -1459,8 +1449,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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 = NumCast(this.Document._panX) + (i % dim) * width - width * dim / 2;
- doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - height * dim / 2;
+ 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;
});
}
@@ -1567,35 +1557,58 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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" />
- ];
+ 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();
+ }
+ }
+ }} />;
}
@computed get placeholder() {
- return <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
+ return <div className="collectionfreeformview-placeholder" style={{ background: 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}
@@ -1610,18 +1623,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
<div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
- {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)}
+ {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)}
<CollectionFreeFormViewPannableContents
isAnnotationOverlay={this.isAnnotationOverlay}
isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
@@ -1640,7 +1642,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
@computed get contentScaling() {
- if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0;
+ if (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent) return 0;
const nw = this.nativeWidth;
const nh = this.nativeHeight;
const hscale = nh ? this.props.PanelHeight() / nh : 1;
@@ -1673,9 +1675,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.backgroundEvents ? "all" : this.props.pointerEvents as any,
transform: `scale(${this.contentScaling || 1})`,
width: `${100 / (this.contentScaling || 1)}%`,
- 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()
+ 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()
}}>
- {this._firstRender || (this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0) ?
+ {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} />}
@@ -1852,59 +1854,4 @@ 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 b10b0912f..08da682bb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -30,9 +30,6 @@ 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;
@@ -137,15 +134,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
})();
e.stopPropagation();
} else if (e.key === "b" && e.ctrlKey) {
- 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();
+ // 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();
+
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 58ea7410d..b0030471d 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -2,7 +2,9 @@ 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';
@@ -18,8 +20,11 @@ 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() {
+export class CollectionGridView extends CollectionSubView(GridSchema) {
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 160134b60..70c8c9436 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -4,7 +4,9 @@ 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 { DocUtils } from '../../../documents/Documents';
@@ -23,6 +25,9 @@ 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
@@ -32,7 +37,7 @@ import "./CollectionLinearView.scss";
* - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
*/
@observer
-export class CollectionLinearView extends CollectionSubView() {
+export class CollectionLinearView extends CollectionSubView(LinearDocument) {
@observable public addMenuToggle = React.createRef<HTMLInputElement>();
@observable private _selectedIndex = -1;
private _dropDisposer?: DragManager.DragDropDisposer;
@@ -73,7 +78,7 @@ export class CollectionLinearView extends CollectionSubView() {
{ fireImmediately: true }
);
}
- protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //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 2bdf92417..ec1cbadd5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -2,9 +2,11 @@ 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 { emptyFunction, returnFalse } from '../../../../Utils';
+import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -14,6 +16,8 @@ import "./CollectionMulticolumnView.scss";
import ResizeBar from './MulticolumnResizer';
import WidthLabel from './MulticolumnWidthLabel';
+type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
+const MulticolumnDocument = makeInterface(documentSchema);
interface WidthSpecifier {
magnitude: number;
@@ -34,7 +38,7 @@ const resolvedUnits = Object.values(DimUnit);
const resizerWidth = 8;
@observer
-export class CollectionMulticolumnView extends CollectionSubView() {
+export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) {
/**
* @returns the list of layout documents whose width unit is
@@ -47,8 +51,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
@computed
private get minimumDim() {
- const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude);
- return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1;
+ return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
}
/**
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 7e2b83230..a2d51e2e7 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -2,9 +2,11 @@ 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 { emptyFunction, returnFalse } from '../../../../Utils';
+import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -14,6 +16,9 @@ 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;
@@ -33,7 +38,7 @@ const resolvedUnits = Object.values(DimUnit);
const resizerHeight = 8;
@observer
-export class CollectionMultirowView extends CollectionSubView() {
+export class CollectionMultirowView extends CollectionSubView(MultirowDocument) {
/**
* @returns the list of layout documents whose width unit is
@@ -46,8 +51,7 @@ export class CollectionMultirowView extends CollectionSubView() {
@computed
private get minimumDim() {
- const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude);
- return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1;
+ return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
}
/**
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index c2bb3b3ac..a439a7998 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -2,18 +2,19 @@ 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, StrCast } from "../../../../fields/Types";
+import { BoolCast, Cast, DateCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
import { ImageField } from "../../../../fields/URLField";
-import { emptyFunction, Utils } from "../../../../Utils";
+import { Utils, emptyFunction } from "../../../../Utils";
import { Docs } from "../../../documents/Documents";
import { DocumentType } from "../../../documents/DocumentTypes";
import { DocumentManager } from "../../../util/DocumentManager";
@@ -28,8 +29,9 @@ import { EditableView } from "../../EditableView";
import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
import { DocumentIconContainer } from "../../nodes/DocumentIcon";
import { OverlayView } from "../../OverlayView";
-import { CollectionView } from "../CollectionView";
import "./CollectionSchemaView.scss";
+import { CollectionView } from "../CollectionView";
+const path = require('path');
// intialize cell properties
export interface CellProps {
@@ -493,8 +495,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 = extname(url.href);
- return url.href.replace(ext, "_o" + ext);
+ const ext = path.extname(url.href); // the extension of the file
+ return url.href.replace(ext, "_o" + path.extname(url.href));
}
render() {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 8b73351d5..b89246489 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -4,6 +4,7 @@ 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";
@@ -46,7 +47,7 @@ const columnTypes: Map<string, ColumnType> = new Map([
]);
@observer
-export class CollectionSchemaView extends CollectionSubView() {
+export class CollectionSchemaView extends CollectionSubView(doc => doc) {
private _previewCont?: HTMLDivElement;
@observable _previewDoc: Doc | undefined = undefined;
@@ -146,43 +147,43 @@ export class CollectionSchemaView extends CollectionSubView() {
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">
@@ -556,7 +557,7 @@ export class CollectionSchemaView extends CollectionSubView() {
style={{
overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white",
pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined,
- width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
+ width: name === "collectionSchemaView-searchContainer" ? "auto" : 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 605481ddf..2219345f6 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -4,6 +4,7 @@ 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 c2b4d0eee..eb7b9a773 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -215,9 +215,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.dataDoc[this.fieldKey + "-recordingStart"] = new DateField();
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.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
}
@@ -604,4 +602,4 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{!this.path ? this.recordingControls : this.playbackControls}
</div>;
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c2a526804..fe34d6687 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,6 +1,7 @@
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";
@@ -22,27 +23,25 @@ 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>() {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, Document>(Document) {
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 : 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 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 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); }
@@ -177,11 +176,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
mixBlendMode,
display: this.ZInd === -99 ? "none" : undefined
}} >
- {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)} />
- }
+ <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 d975baf9b..8da5cd1b1 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -3,7 +3,9 @@ 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";
@@ -15,8 +17,11 @@ 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>() {
+export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument>(ColorDocument) {
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 cbc61ffdb..d47e8340c 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -2,6 +2,8 @@ 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';
@@ -13,10 +15,15 @@ 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>() {
+export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ComparisonDocument>(ComparisonDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); }
protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 433a0bf48..123212608 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, ts } from "../../util/Scripting";
+import { Transformer, Scripting, 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 9ab3171d3..228e1bdcb 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,8 +1,6 @@
@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 7e6ca4248..93cd02d93 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -20,7 +20,6 @@ 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;
@@ -32,7 +31,6 @@ 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, {}> {
@@ -304,16 +302,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) :
- <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>;
+ 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;
}
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 4565f8504..9fcd45e72 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -58,10 +58,9 @@
.documentView-audioBackground {
display: inline-block;
width: 10%;
- height: 25;
position: absolute;
- top: 10px;
- left: 10px;
+ top: 0px;
+ left: 0px;
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 e7de87ea5..707c15bff 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,3 @@
-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";
@@ -10,11 +9,11 @@ import { List } from "../../../fields/List";
import { ObjectField } from "../../../fields/ObjectField";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { BoolCast, Cast, 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, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnVal, simulateMouseClick, Utils } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick, returnEmptyString } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
@@ -24,7 +23,7 @@ import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
import { InteractionUtils } from '../../util/InteractionUtils';
import { LinkManager } from '../../util/LinkManager';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { Scripting } from '../../util/Scripting';
import { SelectionManager } from "../../util/SelectionManager";
import { SharingManager } from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
@@ -49,6 +48,8 @@ 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 {
@@ -109,7 +110,6 @@ 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,12 +179,11 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
}
@observer
-export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() {
+export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps, Document>(Document) {
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;
@@ -205,7 +204,6 @@ 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); }
@@ -241,7 +239,6 @@ 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 => {
@@ -488,7 +485,6 @@ 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(),
@@ -840,13 +836,14 @@ 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} >
+ <div className="documentView-audioBackground"
+ onPointerDown={this.recordAudioAnnotation}
+ onPointerEnter={this.onPointerEnter}
+ style={{ height: 25, position: "absolute", top: 10, left: 10 }}
+ >
<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" />
@@ -856,17 +853,8 @@ 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,
}}>
- {!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}
+ <DocumentContentsView key={1} {...this.props}
docViewPath={this.props.viewPath}
- thumbShown={this.thumbShown}
setContentView={this.setContentView}
scaling={this.contentScaling}
PanelHeight={this.panelHeight}
@@ -878,11 +866,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 || SnappingManager.GetIsDragging() ? (null) :
- <DocumentLinksButton View={this.props.DocumentView()}
- ContentScaling={this.props.ContentScaling}
- Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
- }
+ {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>}
+
{audioView}
</div>;
}
@@ -996,7 +984,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;
@@ -1004,14 +992,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}
@@ -1035,18 +1023,19 @@ 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: string) => {
+ SetValue={undoBatch(input => {
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 + "="), "") as string | number;
+ var value = input.replace(new RegExp(showTitle + "="), "");
if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
if (showTitle.includes("Date") || showTitle === "author") return true;
- Doc.SetInPlace(targetDoc, showTitle, value, true);
+ return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
}
return true;
})}
@@ -1059,17 +1048,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{captionView}
</div>;
}
- @observable _: string = "";
@computed get renderDoc() {
TraceMobx();
- 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;
+ const isButton: boolean = 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 || thumb ? undefined : this.backgroundColor,
+ background: isButton ? undefined : this.backgroundColor,
opacity: this.opacity,
color: StrCast(this.layoutDoc.color, "inherit"),
fontFamily: StrCast(this.Document._fontFamily, "inherit"),
@@ -1078,7 +1065,6 @@ 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}
@@ -1157,7 +1143,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(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
+ @computed get docViewPath() { 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 :
@@ -1295,7 +1281,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
}
-ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
+Scripting.addGlobal(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 c170f9867..f1f802c13 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -3,18 +3,25 @@ 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 './EquationBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
+import './EquationBox.scss';
+
+
+const EquationSchema = createSchema({});
+type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>;
+const EquationDocument = makeInterface(EquationSchema, documentSchema);
@observer
-export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
+export class EquationBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) {
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 ba65acee0..fb8e89da9 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -4,17 +4,18 @@ 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 } from "../../../fields/Schema";
+import { listSpec, makeInterface } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, StrCast, NumCast } 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 { ScriptingGlobals } from "../../util/ScriptingGlobals";
+import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { CollectionTreeView } from "../collections/CollectionTreeView";
import { CollectionView } from "../collections/CollectionView";
@@ -29,8 +30,11 @@ 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>() {
+export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) {
constructor(props: Readonly<FieldViewProps>) {
super(props);
@@ -421,6 +425,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
treeViewHideTitle={true}
focus={returnFalse}
treeViewHideHeaderFields={false}
+ onCheckedClick={this.scriptField}
dontRegisterView={true}
styleProvider={this.FilterStyleProvider}
layerProvider={this.props.layerProvider}
@@ -470,7 +475,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
-ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
+Scripting.addGlobal(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
@@ -480,7 +485,7 @@ ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader:
}
return undefined;
});
-ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
+Scripting.addGlobal(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 3ab0a3ff2..5050fc2d2 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>() {
+export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) {
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 0a4168698..89f70985c 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 } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } 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 } from '../../../fields/Schema';
+import { createSchema, makeInterface } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
@@ -29,11 +29,16 @@ 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",
@@ -42,7 +47,7 @@ const uploadIcons = {
};
@observer
-export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ImageDocument>(ImageDocument) {
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();
@@ -185,7 +190,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 = extname(url.href);
+ const ext = path.extname(url.href);
return url.href.replace(ext, this._curSuffix + ext);
}
@@ -281,7 +286,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: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? "auto" : undefined }} >
+ <div className="imageBox-fader" style={{ overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined }} >
<img key="paths" ref={this._imgRef}
src={srcpath}
style={{ transform, transformOrigin }}
@@ -371,7 +376,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
scrollTop={0}
down={this._marqueeing}
scaling={this.props.scaling}
- docView={this.props.docViewPath().slice(-1)[0]}
+ docView={this.props.docViewPath().lastElement()}
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 c44c8c828..111509fdb 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.name, self: Doc.name, _last_: "any", _readOnly_: "boolean" }, editable: false };
+ const options: ScriptOptions = { addReturn: true, params: { this: "Doc", _last_: "any" }, 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 0015f0b71..dae72bb78 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -2,8 +2,9 @@ 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 { listSpec } from '../../../fields/Schema';
+import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
import { Cast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
@@ -15,13 +16,17 @@ 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)>() {
+export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps), LabelDocument>(LabelDocument) {
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 437d29f39..a7bfb93eb 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -2,6 +2,8 @@ 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';
@@ -21,9 +23,11 @@ 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>() {
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
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 43f4b43fb..879a63248 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,5 +1,7 @@
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";
@@ -7,8 +9,11 @@ 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>() {
+export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(LinkDocument) {
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 aa2130af5..50444c73a 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,11 +1,14 @@
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';
@@ -40,6 +43,9 @@ 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%',
};
@@ -53,6 +59,7 @@ const mapOptions = {
fullscreenControl: false,
};
+dotenv.config({ path: __dirname + '/.env' });
const apiKey = process.env.GOOGLE_MAPS;
const script = document.createElement('script');
@@ -86,7 +93,7 @@ const options = {
} as google.maps.places.AutocompleteOptions;
@observer
-export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>, MapDocument>(MapDocument) {
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 8a68f9647..f44355929 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -248,7 +248,6 @@
.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 9807cee7c..d54b65d92 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,14 +3,17 @@ 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 } from "../../../fields/Doc";
-import { Cast, NumCast, StrCast, ImageCast } from '../../../fields/Types';
+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 { 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";
@@ -19,14 +22,18 @@ 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>() {
+export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, PdfDocument>(PdfDocument) {
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;
@@ -39,7 +46,6 @@ 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);
@@ -48,7 +54,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: any) => this._pdf = pdf));
+ else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf));
}
}
@@ -74,7 +80,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const anchor =
AnchorMenu.Instance?.GetAnchor() ??
Docs.Create.TextanchorDocument({
- title: StrCast(this.rootDoc.title + "@" + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
+ title: StrCast(this.rootDoc.title + "@" + this.layoutDoc._scrollTop?.toFixed(0)),
y: NumCast(this.layoutDoc._scrollTop),
unrendered: true
});
@@ -104,8 +110,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
public prevAnnotation = () => this._pdfViewer?.prevAnnotation();
public nextAnnotation = () => this._pdfViewer?.nextAnnotation();
- 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 backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; };
+ public forwardPage = () => { this.Document._curPage = (this.Document._curPage || 1) + 1; return true; };
public gotoPage = (p: number) => this.Document._curPage = p;
@undoBatch
@@ -136,15 +142,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!this.layoutDoc._showSidebar) this.toggleSidebar();
return this.addDocument(doc, sidebarKey);
}
- 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
+ sidebarBtnDown = (e: React.PointerEvent) => {
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 + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+ const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
- onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
+ this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
return false;
@@ -154,17 +160,16 @@ 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 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 ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
if (preview) {
- this._previewNativeWidth = nativeWidth * sideratio;
- this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
+ this._previewNativeWidth = nativeWidth * ratio;
+ this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
this._showSidebar = true;
}
else {
- this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
- this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
+ this.layoutDoc.nativeWidth = nativeWidth * ratio;
+ this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
});
@@ -180,7 +185,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</button>
</>;
const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
- const curPage = NumCast(this.Document._curPage) || 1;
+ const curPage = 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" }}>
@@ -218,7 +223,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
}}
- onPointerDown={e => this.sidebarBtnDown(e, true)} >
+ onPointerDown={this.sidebarBtnDown} >
<FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
</div>
</div>;
@@ -248,20 +253,18 @@ 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" onPointerDown={e => this.sidebarBtnDown(e, false)} />
+ <div className="pdfBox-background" />
<div style={{
- width: `calc(${100 / scale}% - ${(this.sidebarWidth() + PDFBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
+ width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
height: `${100 / scale}%`,
transform: `scale(${scale})`,
position: "absolute",
@@ -274,7 +277,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
dataDoc={this.dataDoc}
pdf={this._pdf!}
url={this.pdfUrl!.url.pathname}
- isContentActive={this.isPdfContentActive}
+ isContentActive={this.props.isContentActive}
anchorMenuClick={this.anchorMenuClick}
loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
setPdfViewer={this.setPdfViewer}
@@ -304,22 +307,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>();
- static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>();
+ static pdfpromise = new Map<string, Pdfjs.PDFPromise<Pdfjs.PDFDocumentProxy>>();
render() {
TraceMobx();
- if (this._pdf) {
- if (!this.props.thumbShown?.()) {
- return this.renderPdfView;
- }
- return null;
- }
+ if (this._pdf) return this.renderPdfView;
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: any) => PDFBox.pdfcache.set(href, this._pdf = pdf)));
+ PDFBox.pdfpromise.get(href)?.then(action(pdf => 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 dbb567d3a..0c631e5f9 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,17 +1,20 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
// import { Canvas } from '@react-three/fiber';
-import { computed, observable, runInAction } from "mobx";
+import { action, computed, observable, reaction, trace, runInAction } from "mobx";
import { observer } from "mobx-react";
// import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
import { DateField } from "../../../fields/DateField";
-import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
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, OmitKeys, returnFalse, returnOne } from "../../../Utils";
+import { emptyFunction, numberRange, OmitKeys, returnFalse, returnOne, Utils } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { Networking } from "../../Network";
@@ -29,6 +32,9 @@ 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;
@@ -106,7 +112,7 @@ declare class MediaRecorder {
// }
@observer
-export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
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 366c3fc2f..183140cd7 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,19 +1,18 @@
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
import "@webscopeio/react-textarea-autocomplete/style.css";
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, runInAction, trace } 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 { listSpec } from "../../../fields/Schema";
+import { createSchema, listSpec, makeInterface } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
+import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../fields/Types";
import { returnEmptyString } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
import { InteractionUtils } from "../../util/InteractionUtils";
-import { CompileScript, ScriptParam } from "../../util/Scripting";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
+import { CompileScript, Scripting, ScriptParam } from "../../util/Scripting";
import { ScriptManager } from "../../util/ScriptManager";
import { ContextMenu } from "../ContextMenu";
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
@@ -22,10 +21,15 @@ 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>() {
+export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScriptingDocument>(ScriptingDocument) {
private dropDisposer?: DragManager.DragDropDisposer;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
@@ -38,9 +42,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
@observable private _function: boolean = false;
@observable private _spaced: boolean = false;
- @observable private _scriptKeys: any = ScriptingGlobals.getGlobals();
- @observable private _scriptingDescriptions: any = ScriptingGlobals.getDescriptions();
- @observable private _scriptingParams: any = ScriptingGlobals.getParameters();
+ @observable private _scriptKeys: any = Scripting.getGlobals();
+ @observable private _scriptingDescriptions: any = Scripting.getDescriptions();
+ @observable private _scriptingParams: any = Scripting.getParameters();
@observable private _currWord: string = "";
@observable private _suggestions: string[] = [];
@@ -220,9 +224,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
ScriptManager.Instance.addScript(this.dataDoc);
- this._scriptKeys = ScriptingGlobals.getGlobals();
- this._scriptingDescriptions = ScriptingGlobals.getDescriptions();
- this._scriptingParams = ScriptingGlobals.getParameters();
+ this._scriptKeys = Scripting.getGlobals();
+ this._scriptingDescriptions = Scripting.getDescriptions();
+ this._scriptingParams = Scripting.getParameters();
}
// overlays document numbers (ex. d32) over all documents when clicked on
@@ -573,8 +577,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
</div>;
}
- renderFuncListElement(value: string | object) {
- return (typeof value !== "string") ? (null) : <div>
+ renderFuncListElement(value: string) {
+ return <div>
<div style={{ fontSize: "14px" }}>
{value}
</div>
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index b96977f32..92d1f7446 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -2,19 +2,30 @@ 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>() {
+export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocument>(SliderDocument) {
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 fafa39deb..56cb562bc 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -3,10 +3,11 @@ 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, VideoField } from "../../../fields/URLField";
import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
@@ -35,7 +36,7 @@ type VideoDocument = makeInterface<[typeof documentSchema]>;
const VideoDocument = makeInterface(documentSchema);
@observer
-export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
+export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, VideoDocument>(VideoDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
static async convertDataUri(imageUri: string, returnedFilename: string) {
try {
@@ -188,7 +189,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
@action public Snapshot(downX?: number, downY?: number) {
- const width = NumCast(this.layoutDoc._width);
+ const width = (this.layoutDoc._width || 0);
const canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
@@ -202,7 +203,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (!this._videoRef) {
const b = Docs.Create.LabelDocument({
- x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
+ x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1),
_width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
_isLinkButton: true
});
@@ -223,8 +224,7 @@ 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 encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
- const filename = basename(encodedFilename);
+ const filename = path.basename(encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY));
}
@@ -232,12 +232,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
- const width = NumCast(this.layoutDoc._width) || 1;
- const height = NumCast(this.layoutDoc._height);
+ const width = this.layoutDoc._width || 1;
+ const height = this.layoutDoc._height || 0;
const imageSummary = Docs.Create.ImageDocument(url, {
_nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
- 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-"
+ 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-"
});
Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
@@ -282,7 +282,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// @ts-ignore
// vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
this._disposers.reactionDisposer?.();
- this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
+ this._disposers.reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
}
}
@@ -373,7 +373,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(NumCast(this.layoutDoc._currentTimecode)));
+ this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0)));
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 });
@@ -460,7 +460,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(NumCast(this.layoutDoc._currentTimecode)));
+ const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
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}
@@ -719,7 +719,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
style={{
pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
borderRadius,
- overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
+ overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined
}} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
<div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
<div style={{
@@ -755,7 +755,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
scrollTop={0}
down={this._marqueeing}
scaling={this.marqueeFitScaling}
- docView={this.props.docViewPath().slice(-1)[0]}
+ docView={this.props.docViewPath().lastElement()}
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 7dc970496..79289abaa 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -5,12 +5,6 @@
height: 100%;
position: relative;
display: flex;
- .webBox-background {
- width: 100%;
- height: 100%;
- cursor: ew-resize;
- background: lightGray;
- }
.webBox-ui {
position: absolute;
@@ -143,7 +137,6 @@
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 7ff47107e..9956cc36b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -3,20 +3,21 @@ 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 } from "../../../fields/Schema";
+import { listSpec, makeInterface } from "../../../fields/Schema";
import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, ImageCast, NumCast, StrCast } from "../../../fields/Types";
-import { ImageField, WebField } from "../../../fields/URLField";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { 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 { ScriptingGlobals } from "../../util/ScriptingGlobals";
+import { Scripting } from "../../util/Scripting";
import { SnappingManager } from "../../util/SnappingManager";
import { undoBatch } from "../../util/UndoManager";
import { MarqueeOptionsMenu } from "../collections/collectionFreeForm";
@@ -35,18 +36,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>() {
+export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, WebDocument>(WebDocument) {
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();
@@ -62,7 +63,6 @@ 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,7 +76,6 @@ 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);
@@ -104,7 +103,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return true;
}
async componentDidMount() {
- 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)
+ 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.
runInAction(() => {
this._annotationKeySuffix = () => this._urlHash + "-annotations";
@@ -112,48 +111,6 @@ 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 => {
@@ -335,6 +292,12 @@ 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 = "";
@@ -390,46 +353,42 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
}
- forward = (checkAvailable?: boolean) => {
+ @action
+ forward = () => {
const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []);
const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
- 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;
+ 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 false;
}
- back = (checkAvailable?: boolean) => {
+ @action
+ back = () => {
const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
- 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;
+ 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 false;
}
@@ -493,8 +452,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>
);
@@ -553,7 +512,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
@computed get urlContent() {
- if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null);
+ if (this._hackHide) return (null);
const field = this.dataDoc[this.props.fieldKey];
let view;
if (field instanceof HtmlField) {
@@ -571,7 +530,6 @@ 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;
}
@@ -584,37 +542,34 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (!this.layoutDoc._showSidebar) this.toggleSidebar();
return this.addDocumentWrapper(doc, sidebarKey);
}
- @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;
+ sidebarBtnDown = (e: React.PointerEvent) => {
+ 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 + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+ const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
if (ratio >= 1) {
this.layoutDoc.nativeWidth = nativeWidth * ratio;
- onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
+ this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
return false;
- }), action(() => this._draggingSidebar = false), () => this.toggleSidebar());
+ }, emptyFunction, () => 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 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 ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
if (preview) {
- this._previewNativeWidth = nativeWidth * sideratio;
- this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
+ this._previewNativeWidth = nativeWidth * ratio;
+ this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
this._showSidebar = true;
}
else {
- this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
- this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
+ this.layoutDoc.nativeWidth = nativeWidth * ratio;
+ this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
}
});
@@ -678,7 +633,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
return this.props.styleProvider?.(doc, props, property);
}
- pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
+ pointerEvents = () => 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;
@@ -709,17 +664,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
return (
<div className="webBox" ref={this._mainCont}
- 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}>
+ style={{ pointerEvents: this.pointerEvents() }} >
+ <div className={`webBox-container`} style={{ 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
@@ -752,7 +703,6 @@ 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}
@@ -770,11 +720,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
}}
- onPointerDown={e => this.sidebarBtnDown(e, true)} >
+ onPointerDown={this.sidebarBtnDown} >
<FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
</div>
{!this.props.isContentActive() ? (null) : this.searchUI}
</div>);
}
}
-ScriptingGlobals.add(function urlHash(url: string) { return WebBox.urlHash(url); }); \ No newline at end of file
+Scripting.addGlobal(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
deleted file mode 100644
index 08a5746d1..000000000
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ /dev/null
@@ -1,395 +0,0 @@
-/**
- *
- * @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 f3731b8f9..bb4dd8bc9 100644
--- a/src/client/views/nodes/button/ButtonScripts.ts
+++ b/src/client/views/nodes/button/ButtonScripts.ts
@@ -1,14 +1,14 @@
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
+import { Scripting } from "../../../util/Scripting";
import { SelectionManager } from "../../../util/SelectionManager";
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function changeView(view: string) {
+Scripting.addGlobal(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
-ScriptingGlobals.add(function toggleOverlay() {
+Scripting.addGlobal(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 ca13590de..bd103dcf7 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,30 +5,31 @@ 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 } from '../../../../fields/Doc';
+import { Doc, StrListCast, WidthSym, HeightSym } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
-import { createSchema } from '../../../../fields/Schema';
+import { createSchema, makeInterface } from '../../../../fields/Schema';
import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
import { WebField } from '../../../../fields/URLField';
-import { Utils } from '../../../../Utils';
import { DocumentType } from '../../../documents/DocumentTypes';
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
+import { Scripting } from "../../../util/Scripting";
import { SelectionManager } from '../../../util/SelectionManager';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { UndoManager, undoBatch } 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 { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { SetActiveInkColor, ActiveFillColor, SetActiveFillColor, ActiveInkWidth, ActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '.././FieldView';
import { RichTextMenu } from '../formattedText/RichTextMenu';
-import { WebBox } from '../WebBox';
+import { Utils } from '../../../../Utils';
+import { IButtonProps } from './ButtonInterface';
import { FontIconBadge } from './FontIconBadge';
import './FontIconBox.scss';
+import { WebBox } from '../WebBox';
const FontIconSchema = createSchema({
icon: "string",
});
@@ -56,8 +57,11 @@ 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>() {
+export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(FontIconDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
showTemplate = (): void => {
const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
@@ -101,11 +105,16 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
*/
@computed get numberButton() {
const numBtnType: string = StrCast(this.rootDoc.numBtnType);
- const numScript = ScriptCast(this.rootDoc.script);
- const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false });
+ const setValue = (value: number) => {
+ // Script for running the toggle
+ const script: string = StrCast(this.rootDoc.script) + "(" + value + ")";
+ ScriptField.MakeScript(script)?.script.run();
+ };
// Script for checking the outcome of the toggle
- const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
+ const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)";
+ const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result || 0;
+
if (numBtnType === NumButtonType.Slider) {
const dropdown =
@@ -228,37 +237,34 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const script = ScriptCast(this.rootDoc.script);
+ const script: string = StrCast(this.rootDoc.script);
let noviceList: string[] = [];
let text: string | undefined;
let dropdown = true;
let icon: IconProp = "caret-down";
- 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);
- }
+
+ if (script === 'setView') {
+ const selected = SelectionManager.Docs().lastElement();
+ if (selected) {
+ if (StrCast(selected.type) === DocumentType.COL) {
+ text = StrCast(selected._viewType);
} else {
dropdown = false;
- icon = "globe-asia";
- text = "User Default";
+ text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
+ icon = Doc.toIcon(selected);
}
- 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"];
+ } else {
+ dropdown = false;
+ icon = "globe-asia";
+ text = "User Default";
}
- } catch (e) {
- console.log(e);
+ 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"];
}
// Get items to place into the list
@@ -266,12 +272,18 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
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.script.originalScript.startsWith('setFont') ? value : undefined,
+ fontFamily: script === 'setFont' ? value : undefined,
backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
}}
- onClick={() => script.script.run({ value }).result}>
+ onClick={click}>
{value[0].toUpperCase() + value.slice(1)}
</div>;
});
@@ -307,12 +319,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
}
@observable colorPickerClosed: boolean = true;
- @computed get colorScript() { return ScriptCast(this.rootDoc.script); }
+ @computed get colorScript() {
+ const script = StrCast(this.rootDoc.script);
+ return ScriptField.MakeScript(script + '(colValue, checkResult)', { colValue: "string", checkResult: "boolean" });
+ }
colorPicker = (curColor: string) => {
const change = (value: ColorState) => {
const s = this.colorScript;
- s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)();
+ s && undoBatch(() => s.script.run({ colValue: Utils.colorString(value), checkResult: false }).result)();
};
const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -328,7 +343,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
@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({ value: undefined, _readOnly_: true }).result ?? "transparent";
+ const curColor = this.colorScript?.script.run({ colValue: undefined, checkResult: true }).result ?? "transparent";
const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
<div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
@@ -424,34 +439,52 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
@computed get editableText() {
// Script for running the toggle
- const script = ScriptCast(this.rootDoc.script);
+ const script: string = StrCast(this.rootDoc.script);
+
+ // Script for checking the outcome of the toggle
+ const checkScript: string = StrCast(this.rootDoc.script) + "('', true)";
+
// Function to run the script
- const checkResult = script?.script.run({ value: "", _readOnly_: true }).result;
+ const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
- const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result;
+ const setValue = (value: string, shiftDown?: boolean): boolean => {
+ ScriptField.MakeScript(script + "('" + value + "')")?.script.run();
+ return true;
+ };
return (
<div className="menuButton editableText">
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
<div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}>
- <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} />
+ <EditableView GetValue={() => checkResult} 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, backgroundColor, position: "absolute" }}>
+ <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
{this.label}
</div>;
const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
- <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
+ <div className="fontIconBox-label" style={{ color: 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
@@ -460,7 +493,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
switch (this.type) {
case ButtonType.TextButton:
button = (
- <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
+ <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: 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">
@@ -489,7 +522,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
break;
case ButtonType.ToolButton:
button = (
- <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}>
+ <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
{label}
</div>
@@ -501,7 +534,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
break;
case ButtonType.ClickButton:
button = (
- <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}>
+ <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1 }}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
{label}
</div>
@@ -511,7 +544,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
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, backgroundColor }}>
+ <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: 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)} />
@@ -531,14 +564,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setView(view: string) {
+Scripting.addGlobal(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
-ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
+Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
if (checkResult) {
return selected?._backgroundColor ?? "transparent";
@@ -547,7 +580,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
});
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
+Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) {
if (checkResult) {
return Doc.SharingDoc().userColor;
}
@@ -557,7 +590,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
});
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
+Scripting.addGlobal(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;
@@ -579,7 +612,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
**/
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) {
+Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
SelectionManager.Docs().map(doc => doc._fontFamily = font);
const editorView = RichTextMenu.Instance.TextView?.EditorView;
if (checkResult) {
@@ -589,7 +622,7 @@ ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) {
else Doc.UserDoc().fontFamily = font;
});
-ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
+Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
const editorView = RichTextMenu.Instance.TextView?.EditorView;
const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
switch (info) {
@@ -600,7 +633,7 @@ ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color
}
});
-ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
+Scripting.addGlobal(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";
@@ -609,7 +642,7 @@ ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", c
else Doc.UserDoc().textAlign = align;
});
-ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
+Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
@@ -624,8 +657,8 @@ ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", chec
});
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) {
- const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
+ const editorView = RichTextMenu.Instance.TextView?.EditorView;
if (checkResult) {
return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
@@ -636,7 +669,7 @@ ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean
});
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boolean) {
+Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
const editorView = RichTextMenu.Instance.TextView?.EditorView;
@@ -653,18 +686,18 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo
});
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
- const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+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;
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;
});
-ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
+Scripting.addGlobal(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";
@@ -673,7 +706,7 @@ ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold";
});
-ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) {
+Scripting.addGlobal(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";
@@ -682,7 +715,7 @@ ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) {
else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline";
});
-ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
+Scripting.addGlobal(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";
@@ -700,7 +733,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
* setStrokeColor
**/
-ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
if (checkResult) {
return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
Colors.MEDIUM_BLUE : "transparent";
@@ -726,7 +759,7 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole
});
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) {
+Scripting.addGlobal(function setFillColor(color?: string, checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
if (checkResult) {
if (selected?.type === DocumentType.INK) {
@@ -738,7 +771,7 @@ ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean
SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color);
});
-ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) {
+Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
if (checkResult) {
const selected = SelectionManager.Docs().lastElement();
if (selected?.type === DocumentType.INK) {
@@ -751,7 +784,7 @@ ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolea
});
// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boolean) {
+Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
if (checkResult) {
const selected = SelectionManager.Docs().lastElement();
if (selected?.type === DocumentType.INK) {
@@ -767,7 +800,7 @@ ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boole
/** WEB
* webSetURL
**/
-ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
+Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
const selected = SelectionManager.Views().lastElement();
if (selected?.rootDoc.type === DocumentType.WEB) {
if (checkResult) {
@@ -777,26 +810,24 @@ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
//selected.rootDoc.data = new WebField(url);
}
});
-ScriptingGlobals.add(function webForward(checkResult?: boolean) {
- const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
- if (checkResult) {
- return selected?.forward(checkResult) ? undefined : "lightGray";
+Scripting.addGlobal(function webForward() {
+ const selected = SelectionManager.Views().lastElement();
+ if (selected?.rootDoc.type === DocumentType.WEB) {
+ (selected.ComponentView as WebBox).forward();
}
- selected?.forward();
});
-ScriptingGlobals.add(function webBack(checkResult?: boolean) {
- const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
- if (checkResult) {
- return selected?.back(checkResult) ? undefined : "lightGray";
+Scripting.addGlobal(function webBack() {
+ const selected = SelectionManager.Views().lastElement();
+ if (selected?.rootDoc.type === DocumentType.WEB) {
+ (selected.ComponentView as WebBox).back();
}
- selected?.back();
});
/** Schema
* toggleSchemaPreview
**/
-ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
+Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
const selected = SelectionManager.Docs().lastElement();
if (checkResult && selected) {
const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
@@ -815,7 +846,7 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
/** STACK
* groupBy
*/
-ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
+Scripting.addGlobal(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 6192b6829..311d52afa 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,11 +12,13 @@ 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';
@@ -75,10 +77,13 @@ 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)>() {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); }
public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
public static Instance: FormattedTextBox;
@@ -640,7 +645,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.layout as string;
+ this.rootDoc.layout = (this.layoutDoc as Doc).layout as string;
this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
this.rootDoc.isTemplateDoc = false;
this.rootDoc.isTemplateForField = "";
@@ -1125,13 +1130,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
},
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
- 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); }
+ 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); }
},
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
@@ -1418,7 +1423,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.dataDoc[this.fieldKey + "-translation"] = result1 + "\r\n\r\n" + result[0];
}), 1000);
});
- } catch (e: any) { console.log(e.message); }
+ } catch (e) { 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 c76eda859..76a5675de 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 as any, dispatch as any));
+ bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, 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 as any, (tx2: Transaction) => {
+ if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (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 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->", (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-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 as any, dispatch as any));
+ bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, 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<S>) => {
+ if (!deleteSelection(state, (tx: Transaction<Schema<any, any>>) => {
dispatch(updateBullets(tx, schema));
})) {
- if (!joinBackward(state, (tx: Transaction<S>) => {
+ if (!joinBackward(state, (tx: Transaction<Schema<any, any>>) => {
dispatch(updateBullets(tx, schema));
})) {
- if (!selectNodeBackward(state, (tx: Transaction<S>) => {
+ if (!selectNodeBackward(state, (tx: Transaction<Schema<any, any>>) => {
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) as any);
+ dispatch(state.tr.lift(trange, depth));
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 as any)) {
- if (!splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => {
+ if (cr || !newlineInCode(state, dispatch)) {
+ if (!splitListItem(schema.nodes.list_item)(state, (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 bafae84dc..711136469 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -34,9 +34,13 @@ export class RichTextRules {
wrappingInputRule(
/^1\.\s$/,
schema.nodes.ordered_list,
- () => ({ 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
+ () => {
+ 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 } })
),
// A. create alphabetical ordered list
@@ -51,16 +55,20 @@ export class RichTextRules {
(match: any, node: any) => {
return node.childCount + node.attrs.order === +match[1];
},
- ((type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })) as any
+ (type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })
),
// * + - create bullet list
wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.ordered_list,
// match => {
- () => ({ 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
- ),
+ () => {
+ 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" } })),
// ``` create code block
textblockTypeInputRule(/^```$/, schema.nodes.code_block),
@@ -213,7 +221,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 || 0))));
+ tr.selection.anchor - tr.selection.$anchor.nodeBefore!.nodeSize)));
}),
// 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 2e312ee51..2370b7e16 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -6,10 +6,11 @@ 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 } from "../../../../fields/Schema";
+import { listSpec, makeInterface } from "../../../../fields/Schema";
import { ScriptField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils';
@@ -17,7 +18,7 @@ import { Docs } from "../../../documents/Documents";
import { DocumentType } from "../../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { DocumentManager } from "../../../util/DocumentManager";
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
+import { Scripting } from "../../../util/Scripting";
import { SelectionManager } from "../../../util/SelectionManager";
import { undoBatch, UndoManager } from "../../../util/UndoManager";
import { CollectionDockingView } from "../../collections/CollectionDockingView";
@@ -38,8 +39,11 @@ export class PinProps {
hidePresBox?: boolean;
}
+type PresBoxSchema = makeInterface<[typeof documentSchema]>;
+const PresBoxDocument = makeInterface(documentSchema);
+
@observer
-export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
+export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
/**
@@ -2457,7 +2461,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>;
}
}
-ScriptingGlobals.add(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
+Scripting.addGlobal(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 a4ec559f5..238d025dc 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -3,10 +3,11 @@ 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, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
-import { DocUtils } from "../../../documents/Documents";
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../../Utils";
import { DocumentType } from "../../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { DocumentManager } from "../../../util/DocumentManager";
@@ -15,20 +16,36 @@ 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 { StyleProp } from "../../StyleProvider";
import { PresBox } from "./PresBox";
+import { Colors } from "../../global/globalEnums";
+import { StyleProp } from "../../StyleProvider";
import "./PresElementBox.scss";
-import { PresMovement } from "./PresEnums";
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);
/**
* 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>() {
+export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDocument>(PresDocument) {
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 1856c5353..3f7f38bdf 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -26,6 +26,7 @@ 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");
@@ -33,7 +34,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.13.216/build/pdf.worker.js";
+pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.4.456/build/pdf.worker.min.js";
interface IViewerProps extends FieldViewProps {
Document: Doc;
@@ -158,7 +159,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((val, i) =>
+ await Promise.all(this._pageSizes.map<Pdfjs.PDFPromise<any>>((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, {
@@ -218,7 +219,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));
}
@@ -554,7 +555,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.isContentActive() ? "-selected" : "")} ref={this._viewer} />;
+ return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && (this.props.isSelected() || 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 5fe2a5ab1..09cfb2077 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,18 +1,28 @@
-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;
@@ -28,7 +38,7 @@ export interface SearchBoxProps extends FieldViewProps {
* the search panel on the left side of the screen.
*/
@observer
-export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
+export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDocument>(SearchBoxDocument) {
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 e0d328c89..505a8da7e 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -12,7 +12,6 @@ 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";
/**
@@ -65,8 +64,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 as IconLookup} color="white" /></div>
- <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync as IconLookup} color="white" /></div>
+ <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>
</div >;