diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/.DS_Store | bin | 6148 -> 8196 bytes | |||
-rw-r--r-- | src/client/documents/Documents.ts | 5 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 2 | ||||
-rw-r--r-- | src/client/util/Scripting.ts | 27 | ||||
-rw-r--r-- | src/client/views/EditableView.tsx | 9 | ||||
-rw-r--r-- | src/client/views/OverlayView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionViewChromes.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/ScriptingBox.scss | 110 | ||||
-rw-r--r-- | src/client/views/nodes/ScriptingBox.tsx | 412 | ||||
-rw-r--r-- | src/server/database.ts | 1 |
10 files changed, 505 insertions, 64 deletions
diff --git a/src/.DS_Store b/src/.DS_Store Binary files differindex 5b35884bd..d2050d4be 100644 --- a/src/.DS_Store +++ b/src/.DS_Store diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7f5b62f22..9b1912fb6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -711,6 +711,11 @@ export namespace Docs { } export function ButtonDocument(options?: DocumentOptions) { + // const btn = InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), "onClick-rawScript": "-script-" }); + // btn.layoutKey = "layout_onClick"; + // btn.height = 250; + // btn.width = 200; + // btn.layout_onClick = ScriptingBox.LayoutString("onClick"); return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), "onClick-rawScript": "-script-" }); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 496099557..1fdf50dd4 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -736,6 +736,6 @@ export class CurrentUserUtils { } } -Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); +Scripting.addGlobal("setupMobileInkingDoc", function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }); Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); });
\ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index ab577315c..817e6b29d 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -49,12 +49,25 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is export namespace Scripting { export function addGlobal(global: { name: string }): void; export function addGlobal(name: string, global: any): void; - export function addGlobal(nameOrGlobal: any, global?: any) { - let n: string; + + export function addGlobal(global: { name: string }, decription?: string, params?: any): void; + + export function addGlobal(nameOrGlobal: any, global?: any, params?: any) { + let n: any; let obj: any; - if (global !== undefined && typeof nameOrGlobal === "string") { - n = nameOrGlobal; - obj = global; + + if (global !== undefined) { + if (typeof nameOrGlobal === "string") { + n = nameOrGlobal; + obj = global; + } else { + n = nameOrGlobal.name; + obj = [nameOrGlobal]; + obj.push(global); + if (params) { + obj.push(params); + } + } } else if (nameOrGlobal && typeof nameOrGlobal.name === "string") { n = nameOrGlobal.name; obj = nameOrGlobal; @@ -87,6 +100,10 @@ export namespace Scripting { export function getGlobals() { return Object.keys(scriptingGlobals); } + + export function getGlobalObj() { + return _scriptingGlobals; + } } export function scriptingGlobal(constructor: { new(...args: any[]): any }) { diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index e0e205df9..4f3da70d7 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -5,6 +5,7 @@ import * as Autosuggest from 'react-autosuggest'; import { ObjectField } from '../../fields/ObjectField'; import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; import "./EditableView.scss"; +import { DragManager } from '../util/DragManager'; export interface EditableProps { /** @@ -48,6 +49,7 @@ export interface EditableProps { HeadingObject?: SchemaHeaderField | undefined; toggle?: () => void; color?: string | undefined; + onDrop?: any; } /** @@ -77,6 +79,13 @@ export class EditableView extends React.Component<EditableProps> { } } + @action + componentDidMount() { + if (this._ref.current && this.props.onDrop) { + DragManager.MakeDropTarget(this._ref.current, this.props.onDrop.bind(this)); + } + } + _didShow = false; @action diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index cfa869fb2..f6e5e1705 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -12,7 +12,6 @@ import './OverlayView.scss'; import { Scripting } from "../util/Scripting"; import { ScriptingRepl } from './ScriptingRepl'; import { DragManager } from "../util/DragManager"; -import { listSpec } from "../../fields/Schema"; import { List } from "../../fields/List"; export type OverlayDisposer = () => void; diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 29a3e559a..3dc740c25 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -93,7 +93,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro this.props.collapse?.(true); break; } - }) + }); @undoBatch viewChanged = (e: React.ChangeEvent) => { diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss index 43695f00d..d369045f4 100644 --- a/src/client/views/nodes/ScriptingBox.scss +++ b/src/client/views/nodes/ScriptingBox.scss @@ -5,31 +5,117 @@ flex-direction: column; background-color: rgb(241, 239, 235); padding: 10px; + .scriptingBox-inputDiv { display: flex; flex-direction: column; - height: calc(100% - 30px); + height: 100%; + table-layout: fixed; + + overflow-y: hidden; + white-space: nowrap; + + .scriptingBox-wrapper { + width: 100%; + height: 100%; + max-height: calc(100%-30px); + display: flex; + flex-direction: row; + justify-content: center; + overflow-y: scroll; + + .scriptingBox-textArea { + flex: 70; + width: 70%; + height: 100%; + box-sizing: border-box; + resize: none; + padding: 7px; + overflow-y: scroll; + } + + .scriptingBox-plist { + flex: 30; + width: 30%; + height: 100%; + box-sizing: border-box; + resize: none; + padding: 2px; + overflow-y: scroll; + + .scriptingBox-pborder { + background-color: rgb(241, 239, 235); + } + + .scriptingBox-viewBase { + display: flex; + + .scriptingBox-viewPicker { + font-size: 75%; + //text-transform: uppercase; + letter-spacing: 2px; + background: rgb(238, 238, 238); + color: grey; + outline-color: black; + border: none; + padding: 12px 10px 11px 10px; + } + + .scriptingBox-viewPicker:active { + outline-color: black; + } + + .commandEntry-outerDiv { + pointer-events: all; + background-color: gray; + display: flex; + flex-direction: row; + } + } + } + + .scriptingBox-paramNames { + flex: 60; + width: 60%; + box-sizing: border-box; + resize: none; + padding: 7px; + overflow-y: scroll; + } + + .scriptingBox-paramInputs { + flex: 40; + width: 40%; + box-sizing: border-box; + resize: none; + padding: 2px; + overflow-y: scroll; + } + } + .scriptingBox-errorMessage { overflow: auto; + background: "red"; + background-color: "red"; + height: 40px; } + .scripting-params { - background: "beige"; - } - .scriptingBox-textArea { - width: 100%; - height: 100%; - box-sizing: border-box; - resize: none; - padding: 7px; + background: rgb(241, 239, 235); } } .scriptingBox-toolbar { width: 100%; height: 30px; + .scriptingBox-button { - width: 50% + width: 50%; + resize: auto; } - } -} + .scriptingBox-button-third { + width: 33%; + } + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 0944edf60..51d4cb56e 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -1,19 +1,25 @@ -import { action, observable, computed } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; +import { Doc } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; -import { createSchema, makeInterface, listSpec } from "../../../fields/Schema"; +import { List } from "../../../fields/List"; +import { createSchema, listSpec, makeInterface } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; -import { StrCast, ScriptCast, Cast } from "../../../fields/Types"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { returnEmptyString } from "../../../Utils"; +import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from "../../util/InteractionUtils"; -import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting"; +import { CompileScript, ScriptParam, Scripting } from "../../util/Scripting"; +import { ContextMenu } from "../ContextMenu"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import "./ScriptingBox.scss"; import { OverlayView } from "../OverlayView"; import { DocumentIconContainer } from "./DocumentIcon"; -import { List } from "../../../fields/List"; +import "./ScriptingBox.scss"; +import Autosuggest from "react-autosuggest"; +import { emptyFunction } from '../../../Utils'; const ScriptingSchema = createSchema({}); type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>; @@ -21,78 +27,396 @@ const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); @observer export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) { + + private dropDisposer?: DragManager.DragDropDisposer; protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); } - - _overlayDisposer?: () => void; + private _overlayDisposer?: () => void; @observable private _errorMessage: string = ""; + @observable private _applied: boolean = false; + @observable private _suggested: boolean = false; + @observable private _scriptKeys: any = Scripting.getGlobals(); + @observable private _scriptingGlobals: any = Scripting.getGlobalObj(); + @observable private _currWord: string = ""; + @observable private _suggestions: any[] = []; - @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], StrCast(this.layoutDoc[this.props.fieldKey + "-rawScript"])); } - @computed get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), Cast(this.layoutDoc[this.props.fieldKey + "-params"], listSpec("string"), [])); } + // vars included in fields that store parameters types and names and the script itself + @computed get paramsNames() { return this.compileParams.map(p => p.split(":")[0].trim()); } + @computed get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); } + @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], ""); } + @computed get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), []); } set rawScript(value) { this.dataDoc[this.props.fieldKey + "-rawScript"] = value; } - set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = value; } + set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = new List<string>(value); } @action componentDidMount() { - this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript || this.rawScript; + this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript ?? this.rawScript; } componentWillUnmount() { this._overlayDisposer?.(); } + protected createDashEventsTarget = (ele: HTMLDivElement, dropFunc: (e: Event, de: DragManager.DropEvent) => void) => { //used for stacking and masonry view + if (ele) { + this.dropDisposer?.(); + this.dropDisposer = DragManager.MakeDropTarget(ele, dropFunc, this.layoutDoc); + } + } + + // only included in buttons, transforms scripting UI to a button + @action + onFinish = () => { + this.rootDoc.layoutKey = "layout"; + this.rootDoc._height = 50; + this.rootDoc._width = 100; + this.dataDoc.documentText = this.rawScript; + } + + // displays error message + @action + onError = (error: any) => { + this._errorMessage = error?.map((entry: any) => entry.messageText).join(" ") || ""; + } + + // checks if the script compiles using CompileScript method and inputting params @action onCompile = () => { - const params = this.compileParams.reduce((o: ScriptParam, p: string) => { o[p] = "any"; return o; }, {} as ScriptParam); + const params: ScriptParam = {}; + this.compileParams.forEach(p => params[p.split(":")[0].trim()] = p.split(":")[1].trim()); + const result = CompileScript(this.rawScript, { editable: true, transformer: DocumentIconContainer.getTransformer(), params, - typecheck: false + typecheck: true }); - this._errorMessage = isCompileError(result) ? result.errors.map(e => e.messageText).join("\n") : ""; - return this.dataDoc[this.props.fieldKey] = result.compiled ? new ScriptField(result) : undefined; + this.dataDoc.documentText = this.rawScript; + this.dataDoc.data = result.compiled ? new ScriptField(result) : undefined; + this.onError(result.compiled ? undefined : result.errors); } + // checks if the script compiles and then runs the script @action onRun = () => { - this.onCompile()?.script.run({}, err => this._errorMessage = err.map((e: any) => e.messageText).join("\n")); + this.onCompile(); + const bindings: { [name: string]: any } = {}; + this.paramsNames.forEach(key => bindings[key] = this.dataDoc[key]); + // binds vars so user doesnt have to refer to everything as self.<var> + ScriptCast(this.dataDoc.data, null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError); + } + + // checks if the script compiles and switches to applied UI + @action + onApply = () => { + this.onCompile(); + this._applied = true; + } + + @action + onEdit = () => { + this._applied = false; } + // overlays document numbers (ex. d32) over all documents when clicked on onFocus = () => { this._overlayDisposer?.(); this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 }); } + // sets field of the corresponding field key (param name) to be dropped document + @action + onDrop = (e: Event, de: DragManager.DropEvent, fieldKey: string) => { + this.dataDoc[fieldKey] = de.complete.docDragData?.droppedDocuments[0]; + e.stopPropagation(); + } + + // deletes a param from all areas in which it is stored + @action + onDelete = (num: number) => { + this.dataDoc[this.paramsNames[num]] = undefined; + this.compileParams.splice(num, 1); + return true; + } + + // sets field of the param name to the selected value in drop down box + @action + viewChanged = (e: React.ChangeEvent, name: string) => { + //@ts-ignore + this.dataDoc[name] = e.target.selectedOptions[0].value; + } + + // creates a copy of the script document + onCopy = () => { + const copy = Doc.MakeCopy(this.rootDoc, true); + copy.x = NumCast(this.dataDoc.x) + NumCast(this.dataDoc._width); + this.props.addDocument?.(copy); + } + + // adds option to create a copy to the context menu + specificContextMenu = (e: React.MouseEvent): void => { + const existingOptions = ContextMenu.Instance.findByDescription("Options..."); + const options = existingOptions && "subitems" in existingOptions ? existingOptions.subitems : []; + options.push({ description: "Create a Copy", event: this.onCopy, icon: "copy" }); + !existingOptions && ContextMenu.Instance.addItem({ description: "Options...", subitems: options, icon: "hand-point-right" }); + } + + renderErrorMessage() { + return !this._errorMessage ? (null) : <div className="scriptingBox-errorMessage"> {this._errorMessage} </div>; + } + + // rendering when a doc's value can be set in applied UI + renderDoc(parameter: string) { + return <div className="scriptingBox-paramInputs" onFocus={this.onFocus} onBlur={e => this._overlayDisposer?.()} + ref={ele => ele && this.createDashEventsTarget(ele, (e, de) => this.onDrop(e, de, parameter))} > + <EditableView display={"block"} maxHeight={72} height={35} fontSize={14} + contents={this.dataDoc[parameter]?.title ?? "undefined"} + GetValue={() => this.dataDoc[parameter]?.title ?? "undefined"} + SetValue={action((value: string) => { + const script = CompileScript(value, { + addReturn: true, + typecheck: false, + transformer: DocumentIconContainer.getTransformer() + }); + const results = script.compiled && script.run(); + if (results && results.success) { + this._errorMessage = ""; + this.dataDoc[parameter] = results.result; + return true; + } + this._errorMessage = "invalid document"; + return false; + })} + /> + </div>; + } + + // rendering when a string's value can be set in applied UI + renderString(parameter: string) { + return <div className="scriptingBox-paramInputs"> + <EditableView display={"block"} maxHeight={72} height={35} fontSize={14} + contents={this.dataDoc[parameter] ?? "undefined"} + GetValue={() => StrCast(this.dataDoc[parameter]) ?? "undefined"} + SetValue={action((value: string) => { + if (value && value !== " ") { + this._errorMessage = ""; + this.dataDoc[parameter] = value; + return true; + } + return false; + })} + /> + </div>; + } + + // rendering when a number's value can be set in applied UI + renderNumber(parameter: string) { + return <div className="scriptingBox-paramInputs"> + <EditableView display={"block"} maxHeight={72} height={35} fontSize={14} + contents={this.dataDoc[parameter] ?? "undefined"} + GetValue={() => StrCast(this.dataDoc[parameter]) ?? "undefined"} + SetValue={action((value: string) => { + if (value && value !== " ") { + if (parseInt(value)) { + this._errorMessage = ""; + this.dataDoc[parameter] = parseInt(value); + return true; + } + this._errorMessage = "not a number"; + } + return false; + })} + /> + </div>; + } + + // rendering when an enum's value can be set in applied UI (drop down box) + renderEnum(parameter: string, types: string[]) { + return <div className="scriptingBox-paramInputs"> + <div className="scriptingBox-viewBase"> + <div className="commandEntry-outerDiv"> + <select className="scriptingBox-viewPicker" + onPointerDown={e => e.stopPropagation()} + onChange={e => this.viewChanged(e, parameter)} + value={this.dataDoc[parameter]}> + + {types.map(type => + <option className="scriptingBox-viewOption" value={type.trim()}> {type.trim()} </option> + )} + </select> + </div> + </div> + </div>; + } + + // rendering when a boolean's value can be set in applied UI (drop down box) + renderBoolean(parameter: string) { + return <div className="scriptingBox-paramInputs"> + <div className="scriptingBox-viewBase"> + <div className="commandEntry-outerDiv"> + <select className="scriptingBox-viewPicker" + onPointerDown={e => e.stopPropagation()} + onChange={e => this.viewChanged(e, parameter)} + value={this.dataDoc[parameter]}> + <option className="scriptingBox-viewOption" value={"true"}>true </option> + <option className="scriptingBox-viewOption" value={"false"}>false</option> + </select> + </div> + </div> + </div>; + } + + // setting a parameter (checking type and name before it is added) + compileParam(value: string, whichParam?: number) { + if (value.includes(":")) { + const ptype = value.split(":")[1].trim(); + const pname = value.split(":")[0].trim(); + if (ptype === "Doc" || ptype === "string" || ptype === "number" || ptype === "boolean" || ptype.split("|")[1]) { + if ((whichParam !== undefined && pname === this.paramsNames[whichParam]) || !this.paramsNames.includes(pname)) { + this._errorMessage = ""; + if (whichParam !== undefined) { + this.compileParams[whichParam] = value; + } else { + this.compileParams = [...value.split(";").filter(s => s), ...this.compileParams]; + } + return true; + } + this._errorMessage = "this name has already been used"; + } else { + this._errorMessage = "this type is not supported"; + } + } else { + this._errorMessage = "must set type of parameter"; + } + return false; + } + + getSuggestionValue = (suggestion: string) => suggestion; + + renderSuggestion = (suggestion: string) => { + return (null); + } + + @action + handleKeyPress(e: React.ChangeEvent<HTMLTextAreaElement>) { + + this.rawScript = e.target.value; + this._currWord = e.target.value.split(" ")[e.target.value.split(" ").length - 1]; + this._suggestions = []; + + this._scriptKeys.forEach((element: string | string[]) => { + if (element.indexOf(this._currWord) >= 0) { + this._suggestions.push(element); + } + }); + console.log(this._suggestions); + } + + // inputs for scripting div (script box, params box, and params column) + renderScriptingInputs() { + + // params box on bottom + const parameterInput = <div className="scriptingBox-params"> + <EditableView display={"block"} maxHeight={72} height={35} fontSize={22} + contents={""} + GetValue={returnEmptyString} + SetValue={value => value && value !== " " ? this.compileParam(value) : false} + /> + </div>; + + // main scripting input box + const scriptingInputText = + // <Autosuggest + // inputProps={{ value: this.rawScript, onChange: this.handleKeyPress }} + // getSuggestionValue={this.getSuggestionValue} + // suggestions={this._suggestions} + // //alwaysRenderSuggestions={false} + // renderSuggestion={this.renderSuggestion} + // onSuggestionsFetchRequested={emptyFunction} + // onSuggestionsClearRequested={emptyFunction} + // />; + <textarea onFocus={this.onFocus} onBlur={e => this._overlayDisposer?.()} + onChange={e => this.handleKeyPress(e)} + placeholder="write your script here" + value={this.rawScript} + style={{ width: this.compileParams.length > 0 ? "70%" : "100%", resize: "none", height: "100%" }} + />; + + // params column on right side (list) + const definedParameters = !this.compileParams.length ? (null) : + <div className="scriptingBox-plist" style={{ width: "30%" }}> + {this.compileParams.map((parameter, i) => + <div className="scriptingBox-pborder" onKeyPress={e => e.key === "Enter" && this._overlayDisposer?.()} > + <EditableView display={"block"} maxHeight={72} height={35} fontSize={12} background-color={"beige"} + contents={parameter} + GetValue={() => parameter} + SetValue={value => value && value !== " " ? this.compileParam(value, i) : this.onDelete(i)} + /> + </div> + )} + </div>; + + return <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected() && e.stopPropagation()} > + <div className="scriptingBox-wrapper"> + {scriptingInputText} + {definedParameters} + </div> + {parameterInput} + {this.renderErrorMessage()} + </div>; + } + + // toolbar (with compile and apply buttons) for scripting UI + renderScriptingTools() { + const buttonStyle = "scriptingBox-button" + (this.rootDoc.layoutKey === "layout_onClick" ? "third" : ""); + return <div className="scriptingBox-toolbar"> + <button className={buttonStyle} onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button> + <button className={buttonStyle} onPointerDown={e => { this.onApply(); e.stopPropagation(); }}>Apply</button> + {this.rootDoc.layoutKey !== "layout_onClick" ? (null) : + <button className={buttonStyle} onPointerDown={e => { this.onFinish(); e.stopPropagation(); }}>Finish</button>} + </div>; + } + + // inputs UI for params which allows you to set values for each displayed in a list + renderParamsInputs() { + return <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected(true) && e.stopPropagation()} > + {!this.compileParams.length || !this.paramsNames ? (null) : + <div className="scriptingBox-plist"> + {this.paramsNames.map((parameter: string, i: number) => + <div className="scriptingBox-pborder" onKeyPress={e => e.key === "Enter" && this._overlayDisposer?.()} > + <div className="scriptingBox-wrapper"> + <div className="scriptingBox-paramNames"> {`${parameter}:${this.paramsTypes[i]} = `} </div> + {this.paramsTypes[i] === "boolean" ? this.renderBoolean(parameter) : (null)} + {this.paramsTypes[i] === "string" ? this.renderString(parameter) : (null)} + {this.paramsTypes[i] === "number" ? this.renderNumber(parameter) : (null)} + {this.paramsTypes[i] === "Doc" ? this.renderDoc(parameter) : (null)} + {this.paramsTypes[i]?.split("|")[1] ? this.renderEnum(parameter, this.paramsTypes[i].split("|")) : (null)} + </div> + </div>)} + </div>} + </div>; + } + + // toolbar (with edit and run buttons and error message) for params UI + renderParamsTools() { + const buttonStyle = "scriptingBox-button" + (this.rootDoc.layoutKey === "layout_onClick" ? "third" : ""); + return <div className="scriptingBox-toolbar"> + {this.renderErrorMessage()} + <button className={buttonStyle} onPointerDown={e => { this.onEdit(); e.stopPropagation(); }}>Edit</button> + <button className={buttonStyle} onPointerDown={e => { this.onRun(); e.stopPropagation(); }}>Run</button> + {this.rootDoc.layoutKey !== "layout_onClick" ? (null) : + <button className={buttonStyle} onPointerDown={e => { this.onFinish(); e.stopPropagation(); }}>Finish</button>} + </div>; + } + + // renders script UI if _applied = false and params UI if _applied = true render() { - const params = <EditableView - contents={this.compileParams.join(" ")} - display={"block"} - maxHeight={72} - height={35} - fontSize={28} - GetValue={() => ""} - SetValue={value => { this.compileParams = new List<string>(value.split(" ").filter(s => s !== " ")); return true; }} - />; return ( - <div className="scriptingBox-outerDiv" - onWheel={e => this.props.isSelected(true) && e.stopPropagation()}> - <div className="scriptingBox-inputDiv" - onPointerDown={e => this.props.isSelected(true) && e.stopPropagation()} > - <textarea className="scriptingBox-textarea" - placeholder="write your script here" - onChange={e => this.rawScript = e.target.value} - value={this.rawScript} - onFocus={this.onFocus} - onBlur={e => this._overlayDisposer?.()} /> - <div className="scriptingBox-errorMessage" style={{ background: this._errorMessage ? "red" : "" }}>{this._errorMessage}</div> - <div className="scriptingBox-params" >{params}</div> - </div> - {this.rootDoc.layout === "layout" ? <div></div> : (null)} - <div className="scriptingBox-toolbar"> - <button className="scriptingBox-button" onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button> - <button className="scriptingBox-button" onPointerDown={e => { this.onRun(); e.stopPropagation(); }}>Run</button> + <div className={`scriptingBox`} onContextMenu={this.specificContextMenu}> + <div className="scriptingBox-outerDiv" onWheel={e => this.props.isSelected(true) && e.stopPropagation()}> + {!this._applied ? this.renderScriptingInputs() : this.renderParamsInputs()} + {!this._applied ? this.renderScriptingTools() : this.renderParamsTools()} </div> </div> ); } -} +}
\ No newline at end of file diff --git a/src/server/database.ts b/src/server/database.ts index a5f23c4b1..b017f1e3c 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -8,6 +8,7 @@ import { IDatabase, DocumentsCollection } from './IDatabase'; import { MemoryDatabase } from './MemoryDatabase'; import * as mongoose from 'mongoose'; import { Upload } from './SharedMediaTypes'; +import { timeout } from 'async'; export namespace Database { |