aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json50
-rw-r--r--package.json1
-rw-r--r--src/client/util/Scripting.ts122
-rw-r--r--src/client/util/type_decls.d215
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx3
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
6 files changed, 336 insertions, 57 deletions
diff --git a/package-lock.json b/package-lock.json
index 931f91c90..20c871f15 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3376,8 +3376,7 @@
},
"ansi-regex": {
"version": "2.1.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"aproba": {
"version": "1.2.0",
@@ -3395,13 +3394,11 @@
},
"balanced-match": {
"version": "1.0.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3414,18 +3411,15 @@
},
"code-point-at": {
"version": "1.1.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"concat-map": {
"version": "0.0.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"console-control-strings": {
"version": "1.1.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"core-util-is": {
"version": "1.0.2",
@@ -3528,8 +3522,7 @@
},
"inherits": {
"version": "2.0.3",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"ini": {
"version": "1.3.5",
@@ -3539,7 +3532,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -3552,20 +3544,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -3582,7 +3571,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -3655,8 +3643,7 @@
},
"number-is-nan": {
"version": "1.0.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"object-assign": {
"version": "4.1.1",
@@ -3666,7 +3653,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -3742,8 +3728,7 @@
},
"safe-buffer": {
"version": "5.1.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -3773,7 +3758,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -3791,7 +3775,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -3830,13 +3813,11 @@
},
"wrappy": {
"version": "1.0.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"yallist": {
"version": "3.0.3",
- "bundled": true,
- "optional": true
+ "bundled": true
}
}
},
@@ -10403,6 +10384,15 @@
"unpipe": "1.0.0"
}
},
+ "raw-loader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz",
+ "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==",
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "schema-utils": "^1.0.0"
+ }
+ },
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
diff --git a/package.json b/package.json
index ae7a7b25c..57a5c4dca 100644
--- a/package.json
+++ b/package.json
@@ -100,6 +100,7 @@
"prosemirror-transform": "^1.1.3",
"prosemirror-view": "^1.7.1",
"pug": "^2.0.3",
+ "raw-loader": "^1.0.0",
"react": "^16.5.2",
"react-dimensions": "^1.3.1",
"react-dom": "^16.7.0",
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index befb9df4c..44f36fe19 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,12 +1,21 @@
-// import * as ts from "typescript"
-let ts = (window as any).ts;
+import * as ts from "typescript"
+// let ts = (window as any).ts;
import { Opt, Field } from "../../fields/Field";
-import { Document as DocumentImport } from "../../fields/Document";
-import { NumberField as NumberFieldImport, NumberField } from "../../fields/NumberField";
-import { ImageField as ImageFieldImport } from "../../fields/ImageField";
-import { TextField as TextFieldImport, TextField } from "../../fields/TextField";
-import { RichTextField as RichTextFieldImport } from "../../fields/RichTextField";
-import { KeyStore as KeyStoreImport } from "../../fields/KeyStore";
+import { Document } from "../../fields/Document";
+import { NumberField } from "../../fields/NumberField";
+import { ImageField } from "../../fields/ImageField";
+import { TextField } from "../../fields/TextField";
+import { RichTextField } from "../../fields/RichTextField";
+import { KeyStore } from "../../fields/KeyStore";
+import { ListField } from "../../fields/ListField";
+// // @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'
+
export interface ExecutableScript {
(): any;
@@ -14,23 +23,25 @@ export interface ExecutableScript {
compiled: boolean;
}
-function ExecScript(script: string, diagnostics: Opt<any[]>): ExecutableScript {
+function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [name: string]: any }): ExecutableScript {
const compiled = !(diagnostics && diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error));
let func: () => Opt<Field>;
- if (compiled) {
+ if (compiled && script) {
+ let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField];
+ let paramNames = ["KeyStore", ...fieldTypes.map(fn => fn.name)];
+ let params: any[] = [KeyStore, ...fieldTypes]
+ for (let prop in scope) {
+ if (prop === "this") {
+ continue;
+ }
+ paramNames.push(prop);
+ params.push(scope[prop]);
+ }
+ let thisParam = scope["this"];
+ let compiledFunction = new Function(...paramNames, script);
func = function (): Opt<Field> {
- let KeyStore = KeyStoreImport;
- let Document = DocumentImport;
- let NumberField = NumberFieldImport;
- let TextField = TextFieldImport;
- let ImageField = ImageFieldImport;
- let RichTextField = RichTextFieldImport;
- let window = undefined;
- let document = undefined;
- let retVal = eval(script);
-
- return retVal;
+ return compiledFunction.apply(thisParam, params)
};
} else {
func = () => undefined;
@@ -42,10 +53,73 @@ function ExecScript(script: string, diagnostics: Opt<any[]>): ExecutableScript {
});
}
-export function CompileScript(script: string): ExecutableScript {
- let result = (window as any).ts.transpileModule(script, {})
+interface File {
+ fileName: string;
+ content: string;
+}
+
+// class ScriptingCompilerHost implements ts.CompilerHost {
+class ScriptingCompilerHost {
+ files: File[] = [];
+
+ // getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): ts.SourceFile | undefined {
+ getSourceFile(fileName: string, languageVersion: any, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): any | undefined {
+ let contents = this.readFile(fileName);
+ if (contents !== undefined) {
+ return ts.createSourceFile(fileName, contents, languageVersion, true);
+ }
+ return undefined;
+ }
+ // getDefaultLibFileName(options: ts.CompilerOptions): string {
+ getDefaultLibFileName(options: any): string {
+ return 'node_modules/typescript/lib/lib.d.ts' // No idea what this means...
+ }
+ writeFile(fileName: string, content: string) {
+ const file = this.files.find(file => file.fileName === fileName);
+ if (file) {
+ file.content = content;
+ } else {
+ this.files.push({ fileName, content })
+ }
+ }
+ getCurrentDirectory(): string {
+ return '';
+ }
+ getCanonicalFileName(fileName: string): string {
+ return this.useCaseSensitiveFileNames() ? fileName : fileName.toLowerCase();
+ }
+ useCaseSensitiveFileNames(): boolean {
+ return true;
+ }
+ getNewLine(): string {
+ return '\n';
+ }
+ fileExists(fileName: string): boolean {
+ return this.files.some(file => file.fileName === fileName);
+ }
+ readFile(fileName: string): string | undefined {
+ let file = this.files.find(file => file.fileName === fileName);
+ if (file) {
+ return file.content;
+ }
+ return undefined;
+ }
+}
+
+export function CompileScript(script: string, scope?: { [name: string]: any }, addReturn: boolean = false): ExecutableScript {
+ let host = new ScriptingCompilerHost;
+ let funcScript = `(function() {
+ ${addReturn ? `return ${script};` : script}
+ })()`
+ host.writeFile("file.ts", funcScript);
+ host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
+ let program = ts.createProgram(["file.ts"], {}, host);
+ let testResult = program.emit();
+ let outputText = "return " + host.readFile("file.js");
+
+ let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
- return ExecScript(result.outputText, result.diagnostics);
+ return Compile(outputText, diagnostics, scope || {});
}
export function ToField(data: any): Opt<Field> {
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
new file mode 100644
index 000000000..679f73f42
--- /dev/null
+++ b/src/client/util/type_decls.d
@@ -0,0 +1,215 @@
+//@ts-ignore
+declare type PropertyKey = string | number | symbol;
+interface Array<T> {
+ length: number;
+ toString(): string;
+ toLocaleString(): string;
+ pop(): T | undefined;
+ push(...items: T[]): number;
+ concat(...items: ConcatArray<T>[]): T[];
+ concat(...items: (T | ConcatArray<T>)[]): T[];
+ join(separator?: string): string;
+ reverse(): T[];
+ shift(): T | undefined;
+ slice(start?: number, end?: number): T[];
+ sort(compareFn?: (a: T, b: T) => number): this;
+ splice(start: number, deleteCount?: number): T[];
+ splice(start: number, deleteCount: number, ...items: T[]): T[];
+ unshift(...items: T[]): number;
+ indexOf(searchElement: T, fromIndex?: number): number;
+ lastIndexOf(searchElement: T, fromIndex?: number): number;
+ every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
+ some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
+ forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
+ map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
+ filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
+ filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[];
+ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
+ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
+ reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
+ reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
+ reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
+ reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
+
+ [n: number]: T;
+}
+
+interface Function {
+ apply(this: Function, thisArg: any, argArray?: any): any;
+ call(this: Function, thisArg: any, ...argArray: any[]): any;
+ bind(this: Function, thisArg: any, ...argArray: any[]): any;
+ toString(): string;
+
+ prototype: any;
+ readonly length: number;
+
+ // Non-standard extensions
+ arguments: any;
+ caller: Function;
+}
+interface Boolean {
+ valueOf(): boolean;
+}
+interface Number {
+ toString(radix?: number): string;
+ toFixed(fractionDigits?: number): string;
+ toExponential(fractionDigits?: number): string;
+ toPrecision(precision?: number): string;
+ valueOf(): number;
+}
+interface IArguments {
+ [index: number]: any;
+ length: number;
+ callee: Function;
+}
+interface RegExp {
+ readonly flags: string;
+ readonly sticky: boolean;
+ readonly unicode: boolean;
+}
+interface String {
+ codePointAt(pos: number): number | undefined;
+ includes(searchString: string, position?: number): boolean;
+ endsWith(searchString: string, endPosition?: number): boolean;
+ normalize(form: "NFC" | "NFD" | "NFKC" | "NFKD"): string;
+ normalize(form?: string): string;
+ repeat(count: number): string;
+ startsWith(searchString: string, position?: number): boolean;
+ anchor(name: string): string;
+ big(): string;
+ blink(): string;
+ bold(): string;
+ fixed(): string;
+ fontcolor(color: string): string;
+ fontsize(size: number): string;
+ fontsize(size: string): string;
+ italics(): string;
+ link(url: string): string;
+ small(): string;
+ strike(): string;
+ sub(): string;
+ sup(): string;
+}
+interface Object {
+ constructor: Function;
+ toString(): string;
+ toLocaleString(): string;
+ valueOf(): Object;
+ hasOwnProperty(v: PropertyKey): boolean;
+ isPrototypeOf(v: Object): boolean;
+ propertyIsEnumerable(v: PropertyKey): boolean;
+}
+interface ConcatArray<T> {
+ readonly length: number;
+ readonly [n: number]: T;
+ join(separator?: string): string;
+ slice(start?: number, end?: number): T[];
+}
+interface URL {
+ hash: string;
+ host: string;
+ hostname: string;
+ href: string;
+ readonly origin: string;
+ password: string;
+ pathname: string;
+ port: string;
+ protocol: string;
+ search: string;
+ username: string;
+ toJSON(): string;
+}
+
+declare type FieldId = string;
+
+declare abstract class Field {
+ Id: FieldId;
+ abstract ToScriptString(): string;
+ abstract TrySetValue(value: any): boolean;
+ abstract GetValue(): any;
+ abstract Copy(): Field;
+}
+
+declare abstract class BasicField<T> extends Field {
+ constructor(data: T);
+ Data: T;
+ TrySetValue(value: any): boolean;
+ GetValue(): any;
+}
+
+declare class TextField extends BasicField<string>{
+ constructor();
+ constructor(data: string);
+ ToScriptString(): string;
+ Copy(): Field;
+}
+declare class ImageField extends BasicField<URL>{
+ constructor();
+ constructor(data: URL);
+ ToScriptString(): string;
+ Copy(): Field;
+}
+declare class HtmlField extends BasicField<string>{
+ constructor();
+ constructor(data: string);
+ ToScriptString(): string;
+ Copy(): Field;
+}
+declare class NumberField extends BasicField<number>{
+ constructor();
+ constructor(data: number);
+ ToScriptString(): string;
+ Copy(): Field;
+}
+declare class WebField extends BasicField<URL>{
+ constructor();
+ constructor(data: URL);
+ ToScriptString(): string;
+ Copy(): Field;
+}
+declare class ListField<T> extends BasicField<T[]>{
+ constructor();
+ constructor(data: T[]);
+ ToScriptString(): string;
+ Copy(): Field;
+}
+declare class Key extends Field {
+ Name: string;
+ TrySetValue(value: any): boolean;
+ GetValue(): any;
+ Copy(): Field;
+ ToScriptString(): string;
+}
+declare type FIELD_WAITING = "<Waiting>";
+declare type Opt<T> = T | undefined;
+declare type FieldValue<T> = Opt<T> | FIELD_WAITING;
+// @ts-ignore
+declare class Document extends Field {
+ TrySetValue(value: any): boolean;
+ GetValue(): any;
+ Copy(): Field;
+ ToScriptString(): string;
+
+ Width(): number;
+ Height(): number;
+ Scale(): number;
+ Title: string;
+
+ Get(key: Key): FieldValue<Field>;
+ GetAsync(key: Key, callback: (field: Field) => void): boolean;
+ GetOrCreateAsync<T extends Field>(key: Key, ctor: { new(): T }, callback: (field: T) => void): void;
+ GetT<T extends Field>(key: Key, ctor: { new(): T }): FieldValue<T>;
+ GetOrCreate<T extends Field>(key: Key, ctor: { new(): T }): T;
+ GetData<T, U extends Field & { Data: T }>(key: Key, ctor: { new(): U }, defaultVal: T): T;
+ GetHtml(key: Key, defaultVal: string): string;
+ GetNumber(key: Key, defaultVal: number): number;
+ GetText(key: Key, defaultVal: string): string;
+ GetList<T extends Field>(key: Key, defaultVal: T[]): T[];
+ Set(key: Key, field: Field | undefined): void;
+ SetData<T, U extends Field & { Data: T }>(key: Key, value: T, ctor: { new(): U }): void;
+ SetText(key: Key, value: string): void;
+ SetNumber(key: Key, value: number): void;
+ GetPrototype(): FieldValue<Document>;
+ GetAllPrototypes(): Document[];
+ MakeDelegate(): Document;
+}
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index 5e9dcd5d5..a3311f5e2 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -163,7 +163,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
@action
onKeyDown = (e: React.KeyboardEvent<Element>) => {
//if not these keys, make a textbox if preview cursor is active!
- if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
+ if (!e.ctrlKey && !e.altKey) {
if (this._previewCursorVisible) {
//make textbox and add it to this collection
let [x, y] = this.getTransform().transformPoint(this._downX, this._downY); (this._downX, this._downY);
@@ -277,7 +277,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0);
// const panx: number = this.props.Document.GetNumber(KeyStore.PanX, 0) + this.centeringShiftX;
// const pany: number = this.props.Document.GetNumber(KeyStore.PanY, 0) + this.centeringShiftY;
- console.log("center:", this.getLocalTransform().transformPoint(this.centeringShiftX, this.centeringShiftY));
return (
<div className="collectionfreeformview-container"
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 5bcd501cc..1ae8b3418 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -58,7 +58,7 @@ export class CollectionSchemaView extends CollectionViewBase {
return field || "";
}}
SetValue={(value: string) => {
- let script = CompileScript(value);
+ let script = CompileScript(value, undefined, true);
if (!script.compiled) {
return false;
}