diff --git a/package-lock.json b/package-lock.json index 59e63d30..fa72512b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,27 +9,20 @@ "version": "3.11.0", "license": "GPL-3.0", "dependencies": { - "@types/chroma-js": "^2.1.3", "@types/dompurify": "^2.3.4", "@types/emscripten": "^1.39.5", - "@types/js-yaml": "^4.0.5", "atob": "^2.1.x", "binaryen": "^101.0.0", "btoa": "^1.2.x", - "chroma-js": "^2.1.2", "clipboard": "^2.0.6", "dompurify": "^2.4.0", - "error-stack-parser": "^2.0.6", - "fast-png": "^5.0.4", "file-saver": "^2.0.5", "jquery": "^3.6.3", "jszip": "^3.7.0", "localforage": "^1.9.0", "mousetrap": "^1.6.5", "octokat": "^0.10.0", - "preact": "^10.5.14", - "split.js": "^1.6.2", - "yufka": "^2.0.1" + "split.js": "^1.6.2" }, "devDependencies": { "@types/bootbox": "^5.1.3", @@ -45,6 +38,7 @@ "cors": "^2.8.5", "esbuild": "^0.12.29", "express": "^4.18.2", + "fast-png": "^5.0.4", "jsdom": "^21.1.0", "lzg": "^1.0.x", "mocha": "^9.2.0", @@ -865,11 +859,6 @@ "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", "optional": true }, - "node_modules/@types/chroma-js": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/chroma-js/-/chroma-js-2.1.5.tgz", - "integrity": "sha512-LnJmElng1zoH7GOYqIo/EuL7L0/vEh5rc+fKaF4rsylJyjwOkX0pXeBemH25FQAWHifKJWqaRwR0EhC+yDod9A==" - }, "node_modules/@types/dompurify": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", @@ -962,11 +951,6 @@ "@types/sizzle": "*" } }, - "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" - }, "node_modules/@types/lodash": { "version": "4.14.200", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", @@ -988,7 +972,8 @@ "node_modules/@types/pako": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@types/pako/-/pako-1.0.4.tgz", - "integrity": "sha512-Z+5bJSm28EXBSUJEgx29ioWeEEHUh6TiMkZHDhLwjc9wVFH+ressbkmX6waUZc5R3Gobn4Qu5llGxaoflZ+yhA==" + "integrity": "sha512-Z+5bJSm28EXBSUJEgx29ioWeEEHUh6TiMkZHDhLwjc9wVFH+ressbkmX6waUZc5R3Gobn4Qu5llGxaoflZ+yhA==", + "dev": true }, "node_modules/@types/selenium-webdriver": { "version": "4.1.15", @@ -1084,6 +1069,7 @@ "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -1735,11 +1721,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chroma-js": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", - "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==" - }, "node_modules/chromedriver": { "version": "119.0.0", "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-119.0.0.tgz", @@ -2624,6 +2605,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "optional": true, "dependencies": { "stackframe": "^1.3.4" } @@ -2851,6 +2833,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-5.0.4.tgz", "integrity": "sha512-vTNj6yixRnclW6sTlCeH6sNRLBOhM5ITmlo1LSU5ojKEc2e9kZkqXPo2xzBxKb61MBCXRXBcr8qJztOHr2O6WQ==", + "dev": true, "dependencies": { "@types/pako": "^1.0.1", "iobuffer": "^5.0.2", @@ -2860,7 +2843,8 @@ "node_modules/fast-png/node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true }, "node_modules/fd-slicer": { "version": "1.1.0", @@ -3617,7 +3601,8 @@ "node_modules/iobuffer": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.3.2.tgz", - "integrity": "sha512-kO3CjNfLZ9t+tHxAMd+Xk4v3D/31E91rMs1dHrm7ikEQrlZ8mLDbQ4z3tZfDM48zOkReas2jx8MWSAmN9+c8Fw==" + "integrity": "sha512-kO3CjNfLZ9t+tHxAMd+Xk4v3D/31E91rMs1dHrm7ikEQrlZ8mLDbQ4z3tZfDM48zOkReas2jx8MWSAmN9+c8Fw==", + "dev": true }, "node_modules/ip-regex": { "version": "4.3.0", @@ -4668,14 +4653,6 @@ "node": ">=0.8.0" } }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -6174,15 +6151,6 @@ "node": ">= 0.4.0" } }, - "node_modules/preact": { - "version": "10.12.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", - "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -6903,12 +6871,6 @@ "source-map": "^0.6.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead" - }, "node_modules/spawn-wrap": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", @@ -7002,7 +6964,8 @@ "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "optional": true }, "node_modules/stacktrace-parser": { "version": "0.1.10", @@ -7983,18 +7946,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yufka": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yufka/-/yufka-2.1.1.tgz", - "integrity": "sha512-/8gX42mxcoJ21pdGbEg2w/6l0xlL7XFkFarcLmq2upxt6PTf7ehHsPnQQibGd6yLP1odMh0F04eZMh/gZhQHIQ==", - "dependencies": { - "acorn": "^8.3.0", - "magic-string": "^0.25.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/yup": { "version": "0.32.11", "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", @@ -8692,11 +8643,6 @@ "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", "optional": true }, - "@types/chroma-js": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/chroma-js/-/chroma-js-2.1.5.tgz", - "integrity": "sha512-LnJmElng1zoH7GOYqIo/EuL7L0/vEh5rc+fKaF4rsylJyjwOkX0pXeBemH25FQAWHifKJWqaRwR0EhC+yDod9A==" - }, "@types/dompurify": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", @@ -8788,11 +8734,6 @@ "@types/sizzle": "*" } }, - "@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" - }, "@types/lodash": { "version": "4.14.200", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", @@ -8814,7 +8755,8 @@ "@types/pako": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@types/pako/-/pako-1.0.4.tgz", - "integrity": "sha512-Z+5bJSm28EXBSUJEgx29ioWeEEHUh6TiMkZHDhLwjc9wVFH+ressbkmX6waUZc5R3Gobn4Qu5llGxaoflZ+yhA==" + "integrity": "sha512-Z+5bJSm28EXBSUJEgx29ioWeEEHUh6TiMkZHDhLwjc9wVFH+ressbkmX6waUZc5R3Gobn4Qu5llGxaoflZ+yhA==", + "dev": true }, "@types/selenium-webdriver": { "version": "4.1.15", @@ -8906,7 +8848,8 @@ "acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "devOptional": true }, "acorn-globals": { "version": "7.0.1", @@ -9400,11 +9343,6 @@ "readdirp": "~3.6.0" } }, - "chroma-js": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", - "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==" - }, "chromedriver": { "version": "119.0.0", "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-119.0.0.tgz", @@ -10096,6 +10034,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "optional": true, "requires": { "stackframe": "^1.3.4" } @@ -10270,6 +10209,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-5.0.4.tgz", "integrity": "sha512-vTNj6yixRnclW6sTlCeH6sNRLBOhM5ITmlo1LSU5ojKEc2e9kZkqXPo2xzBxKb61MBCXRXBcr8qJztOHr2O6WQ==", + "dev": true, "requires": { "@types/pako": "^1.0.1", "iobuffer": "^5.0.2", @@ -10279,7 +10219,8 @@ "pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true } } }, @@ -10859,7 +10800,8 @@ "iobuffer": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.3.2.tgz", - "integrity": "sha512-kO3CjNfLZ9t+tHxAMd+Xk4v3D/31E91rMs1dHrm7ikEQrlZ8mLDbQ4z3tZfDM48zOkReas2jx8MWSAmN9+c8Fw==" + "integrity": "sha512-kO3CjNfLZ9t+tHxAMd+Xk4v3D/31E91rMs1dHrm7ikEQrlZ8mLDbQ4z3tZfDM48zOkReas2jx8MWSAmN9+c8Fw==", + "dev": true }, "ip-regex": { "version": "4.3.0", @@ -11707,14 +11649,6 @@ } } }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -12871,11 +12805,6 @@ "integrity": "sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ==", "dev": true }, - "preact": { - "version": "10.12.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", - "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==" - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -13463,11 +13392,6 @@ "source-map": "^0.6.0" } }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, "spawn-wrap": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", @@ -13556,7 +13480,8 @@ "stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "optional": true }, "stacktrace-parser": { "version": "0.1.10", @@ -14309,15 +14234,6 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "devOptional": true }, - "yufka": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yufka/-/yufka-2.1.1.tgz", - "integrity": "sha512-/8gX42mxcoJ21pdGbEg2w/6l0xlL7XFkFarcLmq2upxt6PTf7ehHsPnQQibGd6yLP1odMh0F04eZMh/gZhQHIQ==", - "requires": { - "acorn": "^8.3.0", - "magic-string": "^0.25.2" - } - }, "yup": { "version": "0.32.11", "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", diff --git a/package.json b/package.json index 1f75d349..f9a87a73 100644 --- a/package.json +++ b/package.json @@ -10,27 +10,20 @@ }, "license": "GPL-3.0", "dependencies": { - "@types/chroma-js": "^2.1.3", "@types/dompurify": "^2.3.4", "@types/emscripten": "^1.39.5", - "@types/js-yaml": "^4.0.5", "atob": "^2.1.x", "binaryen": "^101.0.0", "btoa": "^1.2.x", - "chroma-js": "^2.1.2", "clipboard": "^2.0.6", "dompurify": "^2.4.0", - "error-stack-parser": "^2.0.6", - "fast-png": "^5.0.4", "file-saver": "^2.0.5", "jquery": "^3.6.3", "jszip": "^3.7.0", "localforage": "^1.9.0", "mousetrap": "^1.6.5", "octokat": "^0.10.0", - "preact": "^10.5.14", - "split.js": "^1.6.2", - "yufka": "^2.0.1" + "split.js": "^1.6.2" }, "devDependencies": { "@types/bootbox": "^5.1.3", @@ -46,6 +39,7 @@ "cors": "^2.8.5", "esbuild": "^0.12.29", "express": "^4.18.2", + "fast-png": "^5.0.4", "jsdom": "^21.1.0", "lzg": "^1.0.x", "mocha": "^9.2.0", diff --git a/src/common/script/env.ts b/src/common/script/env.ts deleted file mode 100644 index ee42f0f7..00000000 --- a/src/common/script/env.ts +++ /dev/null @@ -1,294 +0,0 @@ - -import { WorkerError } from "../workertypes"; -import ErrorStackParser = require("error-stack-parser"); -import yufka from 'yufka'; -import * as bitmap from "./lib/bitmap"; -import * as io from "./lib/io"; -import * as output from "./lib/output"; -import * as color from "./lib/color"; -import * as scriptui from "./lib/scriptui"; - -export const PROP_CONSTRUCTOR_NAME = "$$consname"; - -export interface Cell { - id: string; - object?: any; -} - -export interface RunResult { - cells: Cell[]; - state: {}; -} - -const IMPORTS = { - 'bitmap': bitmap, - 'io': io, - 'output': output, - 'color': color, - 'ui': scriptui, -} - -const LINE_NUMBER_OFFSET = 3; // TODO: shouldnt need? - -const GLOBAL_BADLIST = [ - 'eval' -] - -const GLOBAL_GOODLIST = [ - 'eval', // 'eval' can't be defined or assigned to in strict mode code - 'Math', 'JSON', - 'parseFloat', 'parseInt', 'isFinite', 'isNaN', - 'String', 'Symbol', 'Number', 'Object', 'Boolean', 'NaN', 'Infinity', 'Date', 'BigInt', - 'Set', 'Map', 'RegExp', 'Array', 'ArrayBuffer', 'DataView', - 'Float32Array', 'Float64Array', - 'Int8Array', 'Int16Array', 'Int32Array', - 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Uint8ClampedArray', -] - -class RuntimeError extends Error { - constructor(public loc: acorn.SourceLocation, msg: string) { - super(msg); - } -} - -function setConstructorName(o: object) : void { - let name = Object.getPrototypeOf(o)?.constructor?.name; - if (name != null && name != 'Object') { - o[PROP_CONSTRUCTOR_NAME] = name; - } -} - -export class Environment { - preamble: string; - postamble: string; - obj: {}; - seq: number; - declvars : {[name : string] : acorn.Node}; - builtins : {} - - constructor( - public readonly globalenv: any, - public readonly path: string - ) { - var badlst = Object.getOwnPropertyNames(this.globalenv).filter(name => GLOBAL_GOODLIST.indexOf(name) < 0); - this.builtins = { - print: (...args) => this.print(args), - ...IMPORTS - } - this.preamble = `'use strict';var ${badlst.join(',')};`; - for (var impname in this.builtins) { - this.preamble += `var ${impname}=$$.${impname};` - } - this.preamble += '{\n'; - this.postamble = '\n}'; - } - error(varname: string, msg: string) { - let obj = this.declvars && this.declvars[varname]; - console.log('ERROR', varname, obj, this); - throw new RuntimeError(obj && obj.loc, msg); - } - print(args: any[]) { - if (args && args.length > 0 && args[0] != null) { - this.obj[`$print__${this.seq++}`] = args.length == 1 ? args[0] : args; - } - } - preprocess(code: string): string { - this.declvars = {}; - this.seq = 0; - let options = { - // https://www.npmjs.com/package/magic-string#sgeneratemap-options- - sourceMap: { - file: this.path, - source: this.path, - hires: false, - includeContent: false - }, - // https://github.com/acornjs/acorn/blob/master/acorn/README.md - acorn: { - ecmaVersion: 6 as any, - locations: true, - allowAwaitOutsideFunction: true, - allowReturnOutsideFunction: true, - allowReserved: true, - } - }; - const result = yufka(code, options, (node, { update, source, parent }) => { - const isTopLevel = () => { - return parent() && parent().type === 'ExpressionStatement' && parent(2) && parent(2).type === 'Program'; - } - const convertTopToPrint = () => { - if (isTopLevel()) { - let printkey = `$print__${this.seq++}`; - update(`this.${printkey} = io.data.load(${source()}, ${JSON.stringify(printkey)})`); - //update(`print(${source()});`) - } - } - const left = node['left']; - switch (node.type) { - // add preamble, postamble - case 'Program': - update(`${this.preamble}${source()}${this.postamble}`) - break; - // error on forbidden keywords - case 'Identifier': - if (GLOBAL_BADLIST.indexOf(source()) >= 0) { - update(`__FORBIDDEN__KEYWORD__${source()}__`) // TODO? how to preserve line number? - } else { - convertTopToPrint(); - } - break; - // x = expr --> var x = expr (first use) - case 'AssignmentExpression': - if (isTopLevel()) { - if (left && left.type === 'Identifier') { - if (!this.declvars[left.name]) { - update(`var ${left.name}=io.data.load(this.${source()}, ${JSON.stringify(left.name)})`) - this.declvars[left.name] = left; - } else { - update(`${left.name}=this.${source()}`) - } - } - } - break; - // convert lone expressions to print() - case 'UnaryExpression': - case 'BinaryExpression': - case 'CallExpression': - case 'MemberExpression': - convertTopToPrint(); - break; - // literal comments - case 'Literal': - if (typeof node['value'] === 'string' && isTopLevel()) { - update(`this.$doc__${this.seq++} = { literaltext: ${source()} };`); - } else { - convertTopToPrint(); - } - break; - } - }); - return result.toString(); - } - async run(code: string): Promise { - // TODO: split into cells based on "--" linebreaks? - code = this.preprocess(code); - this.obj = {}; - const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor; - const fn = new AsyncFunction('$$', code).bind(this.obj, this.builtins); - await fn.call(this); - this.checkResult(this.obj, new Set(), []); - } - // https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm - // TODO: return initial location of thingie - checkResult(o, checked: Set, fullkey: string[]) { - if (o == null) return; - if (checked.has(o)) return; - if (typeof o === 'object') { - setConstructorName(o); - delete o.$$callback; // clear callbacks (TODO? put somewhere else?) - if (o.length > 100) return; // big array, don't bother - if (o.BYTES_PER_ELEMENT > 0) return; // typed array, don't bother - checked.add(o); // so we don't recurse if cycle - function prkey() { return fullkey.join('.') } - // go through all object properties recursively - for (var [key, value] of Object.entries(o)) { - if (value == null && fullkey.length == 0 && !key.startsWith("$")) { - this.error(key, `"${key}" has no value.`) - } - fullkey.push(key); - if (typeof value === 'function') { - if (fullkey.length == 1) - this.error(fullkey[0], `"${prkey()}" is a function. Did you forget to pass parameters?`); // TODO? did you mean (needs to see entire expr) - else - this.error(fullkey[0], `This expression may be incomplete, or it contains a function object: ${prkey()}`); // TODO? did you mean (needs to see entire expr) - } - if (typeof value === 'symbol') { - this.error(fullkey[0], `"${prkey()}" is a Symbol, and can't be used.`) // TODO? - } - if (value instanceof Promise) { - this.error(fullkey[0], `"${prkey()}" is unresolved. Use "await" before expression.`) // TODO? - } - this.checkResult(value, checked, fullkey); - fullkey.pop(); - } - } - } - render(): Cell[] { - var cells = []; - for (var [key, value] of Object.entries(this.obj)) { - if (typeof value === 'function') { - // TODO: find other values, functions embedded in objects? - } else { - var cell: Cell = { id: key, object: value }; - cells.push(cell); - } - } - return cells; - } - extractErrors(e: Error): WorkerError[] { - let loc = e['loc']; - if (loc && loc.start && loc.end) { - return [{ - path: this.path, - msg: e.message, - line: loc.start.line, - start: loc.start.column, - end: loc.end.line, - }] - } - if (loc && loc.line != null) { - return [{ - path: this.path, - msg: e.message, - line: loc.line, - start: loc.column, - }] - } - // TODO: Cannot parse given Error object? - let frames = ErrorStackParser.parse(e); - let frame = frames.findIndex(f => f.functionName === 'anonymous'); - let errors = []; - // if ErrorStackParser fails, resort to regex - if (frame < 0 && e.stack != null) { - let m = /.anonymous.:(\d+):(\d+)/g.exec(e.stack); - if (m != null) { - errors.push( { - path: this.path, - msg: e.message, - line: parseInt(m[1]) - LINE_NUMBER_OFFSET, - }); - } - } - // otherwise iterate thru all the frames - while (frame >= 0) { - console.log(frames[frame]); - if (frames[frame].fileName.endsWith('Function')) { - // TODO: use source map - errors.push( { - path: this.path, - msg: e.message, - line: frames[frame].lineNumber - LINE_NUMBER_OFFSET, - //start: frames[frame].columnNumber, - } ); - } - --frame; - } - // if no stack frames parsed, last resort error msg - if (errors.length == 0) { - errors.push( { - path: this.path, - msg: e.message, - line: 0 - } ); - } - return errors; - } - commitLoadableState() { - // TODO: visit children? - for (let [key, value] of Object.entries(this.obj)) { - let loadable = value as io.Loadable; - io.data.save(loadable, key); - } - return io.$$getData(); - } -} diff --git a/src/common/script/lib/bitmap.ts b/src/common/script/lib/bitmap.ts deleted file mode 100644 index 187e00e1..00000000 --- a/src/common/script/lib/bitmap.ts +++ /dev/null @@ -1,474 +0,0 @@ - -// TODO: dynamic import -import * as fastpng from 'fast-png'; -import { Palette } from './color'; -import * as io from './io' -import * as color from './color' -import { coerceToArray, findIntegerFactors, RGBA } from '../../util'; - -export type PixelMapFunction = (x: number, y: number) => number; - -export abstract class AbstractBitmap { - aspect? : number; // aspect ratio, null == default == 1:1 - style? : {} = {}; // CSS styles (TODO: other elements?) - - constructor( - public readonly width: number, - public readonly height: number, - ) { - } - abstract blank(width: number, height: number) : AbstractBitmap; - abstract setarray(arr: ArrayLike) : void; - abstract set(x: number, y: number, val: number) : void; - abstract get(x: number, y: number): number; - abstract getrgba(x: number, y: number): number; - - inbounds(x: number, y: number): boolean { - return (x >= 0 && x < this.width && y >= 0 && y < this.height); - } - assign(fn: ArrayLike | PixelMapFunction) : void { - if (typeof fn === 'function') { - for (let y=0; y { - let bmp = this.blank(this.width, this.height); - bmp.assign((x,y) => this.get(x,y)); - return bmp; - } - crop(srcx: number, srcy: number, width: number, height: number) { - let dest = this.blank(width, height); - dest.assign((x, y) => this.get(x + srcx, y + srcy)); - return dest; - } - blit(src: BitmapType, - destx: number, desty: number, - srcx: number, srcy: number) - { - destx |= 0; - desty |= 0; - srcx |= 0; - srcy |= 0; - for (var y=0; y { - public readonly rgba: Uint32Array - - constructor( - width: number, - height: number, - initial?: Uint32Array | PixelMapFunction - ) { - super(width, height); - this.rgba = new Uint32Array(this.width * this.height); - if (initial) this.assign(initial); - } - setarray(arr: ArrayLike) { - this.rgba.set(arr); - } - set(x: number, y: number, rgba: number) { - if (this.inbounds(x,y)) this.rgba[y * this.width + x] = rgba; - } - get(x: number, y: number): number { - return this.inbounds(x,y) ? this.rgba[y * this.width + x] : 0; - } - getrgba(x: number, y: number): number { - return this.get(x, y); - } - blank(width?: number, height?: number) : RGBABitmap { - return new RGBABitmap(width || this.width, height || this.height); - } - clone() : RGBABitmap { - let bitmap = this.blank(this.width, this.height); - bitmap.rgba.set(this.rgba); - return bitmap; - } -} - -export abstract class MappedBitmap extends AbstractBitmap { - public readonly pixels: Uint8Array - - constructor( - width: number, - height: number, - public readonly bpp: number, - initial?: Uint8Array | PixelMapFunction - ) { - super(width, height); - if (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8) - throw new Error(`Invalid bits per pixel: ${bpp}`); - this.pixels = new Uint8Array(this.width * this.height); - if (initial) this.assign(initial); - } - setarray(arr: ArrayLike) { - this.pixels.set(arr); - } - set(x: number, y: number, index: number) { - if (this.inbounds(x,y)) this.pixels[y * this.width + x] = index; - } - get(x: number, y: number): number { - return this.inbounds(x,y) ? this.pixels[y * this.width + x] : 0; - } -} - -function getbpp(x : number | Palette) : number { - if (typeof x === 'number') return x; - if (x instanceof Palette) { - if (x.colors.length <= 2) return 1; - else if (x.colors.length <= 4) return 2; - else if (x.colors.length <= 16) return 4; - } - return 8; -} - -export class IndexedBitmap extends MappedBitmap { - public palette: Palette; - - constructor( - width: number, - height: number, - bppOrPalette: number | Palette, - initial?: Uint8Array | PixelMapFunction - ) { - super(width, height, getbpp(bppOrPalette), initial); - this.palette = bppOrPalette instanceof Palette - ? bppOrPalette - : color.palette.colors(1 << this.bpp); - } - getrgba(x: number, y: number): number { - return this.palette && this.palette.colors[this.get(x, y)]; - } - blank(width?: number, height?: number, newPalette?: Palette) : IndexedBitmap { - let bitmap = new IndexedBitmap(width || this.width, height || this.height, newPalette || this.palette); - return bitmap; - } - clone() : IndexedBitmap { - let bitmap = this.blank(this.width, this.height); - bitmap.pixels.set(this.pixels); - return bitmap; - } -} - -export function rgba(width: number, height: number, initial?: Uint32Array | PixelMapFunction) { - return new RGBABitmap(width, height, initial); -} - -export function indexed(width: number, height: number, bpp: number, initial?: Uint8Array | PixelMapFunction) { - return new IndexedBitmap(width, height, bpp, initial); -} - -export type BitmapType = RGBABitmap | IndexedBitmap; - -// TODO: check arguments -export function decode(arr: Uint8Array, fmt: PixelEditorImageFormat) { - var pixels = convertWordsToImages(arr, fmt); - // TODO: guess if missing w/h/count? - // TODO: reverse mapping - // TODO: maybe better composable functions - let bpp = (fmt.bpp||1) * (fmt.np||1); - return pixels.map(data => new IndexedBitmap(fmt.w, fmt.h, bpp, data)); -} - -export interface BitmapAnalysis { - min: {w: number, h: number}; - max: {w: number, h: number}; -} - -export function analyze(bitmaps: BitmapType[]) { - bitmaps = coerceToArray(bitmaps); - let r = {min:{w:0,h:0}, max:{w:0,h:0}}; - for (let bmp of bitmaps) { - if (!(bmp instanceof AbstractBitmap)) return null; - r.min.w = Math.min(bmp.width); - r.max.w = Math.max(bmp.width); - r.min.h = Math.min(bmp.height); - r.max.h = Math.max(bmp.height); - } - return r; -} - -export interface MontageOptions { - analysis?: BitmapAnalysis; - gap?: number; - aspect?: number; -} - -export function montage(bitmaps: BitmapType[], options?: MontageOptions) { - bitmaps = coerceToArray(bitmaps); - let minmax = (options && options.analysis) || analyze(bitmaps); - if (minmax == null) throw new Error(`Expected an array of bitmaps`); - let hitrects = []; - let aspect = (options && options.aspect) || 1; - let gap = (options && options.gap) || 0; - if (minmax.min.w == minmax.max.w && minmax.min.h == minmax.max.h) { - let totalPixels = minmax.min.w * minmax.min.h * bitmaps.length; - let factors = findIntegerFactors(totalPixels, minmax.max.w, minmax.max.h, aspect); - let columns = Math.ceil(factors.a / minmax.min.w); // TODO: rounding? - let rows = Math.ceil(factors.b / minmax.min.h); - let result = new RGBABitmap(factors.a + gap * (columns-1), factors.b + gap * (rows-1)); - let x = 0; - let y = 0; - bitmaps.forEach((bmp) => { - result.blit(bmp, x, y, 0, 0); - hitrects.push({x, y, w: bmp.width, h: bmp.height }) - x += bmp.width + gap; - if (x >= result.width) { - x = 0; - y += bmp.height + gap; - } - }) - return result; - } else { - throw new Error(`combine() only supports uniformly-sized images right now`); // TODO - } -} - -///// - -export namespace png { - export function read(url: string): BitmapType { - return decode(io.readbin(url)); - } - export function decode(data: Uint8Array): BitmapType { - let png = fastpng.decode(data); - return convertToBitmap(png); - } - function convertToBitmap(png: fastpng.IDecodedPNG): BitmapType { - if (png.palette && png.depth <= 8) { - return convertIndexedToBitmap(png); - } else { - return convertRGBAToBitmap(png); - } - } - function convertIndexedToBitmap(png: fastpng.IDecodedPNG): IndexedBitmap { - var palarr = png.palette as [number, number, number, number][]; - var palette = new Palette(palarr); - let bitmap = new IndexedBitmap(png.width, png.height, png.depth); - if (png.depth == 8) { - bitmap.pixels.set(png.data); - } else { - let pixperbyte = Math.floor(8 / png.depth); - let mask = (1 << png.depth) - 1; - for (let i = 0; i < bitmap.pixels.length; i++) { - var bofs = (i % pixperbyte) * png.depth; - let val = png.data[Math.floor(i / pixperbyte)]; - bitmap.pixels[i] = (val >> bofs) & mask; - } - } - bitmap.palette = palette; - // TODO: aspect etc - return bitmap; - } - function convertRGBAToBitmap(png: fastpng.IDecodedPNG): RGBABitmap { - const bitmap = new RGBABitmap(png.width, png.height); - const rgba : [number,number,number,number] = [0, 0, 0, 0]; - for (let i = 0; i < bitmap.rgba.length; i++) { - for (let j = 0; j < 4; j++) - rgba[j] = png.data[i * 4 + j]; - bitmap.rgba[i] = color.rgba(rgba); - } - // TODO: aspect etc - return bitmap; - } -} - -export namespace font { - interface Font { - maxheight: number; - glyphs: { [code: number]: Glyph }; - properties: {}; - } - class Glyph extends IndexedBitmap { - constructor(width: number, height: number, bpp: number, - public readonly code: number, - public readonly yoffset: number) { - super(width, height, bpp); - } - } - export function read(url: string) { - if (url.endsWith('.yaff')) return decodeyafflines(io.readlines(url)); - if (url.endsWith('.draw')) return decodedrawlines(io.readlines(url)); - throw new Error(`Can't figure out font format for "${url}"`); - } - export function decodeglyph(glines: string[], curcode: number, yoffset: number): Glyph { - let width = 0; - for (var gline of glines) width = Math.max(width, gline.length); - let g = new Glyph(width, glines.length, 1, curcode, yoffset); - for (var y = 0; y < glines.length; y++) { - let gline = glines[y]; - for (var x = 0; x < gline.length; x++) { - let ch = gline[x]; - g.set(x, y, ch==='@' || ch==='#' ? 1 : 0); // TODO: provide mapping - } - } - return g; - } - // https://github.com/robhagemans/monobit - export function decodeyafflines(lines: string[]): Font { - let maxheight = 0; - let properties = {}; - let glyphs = {}; - let yoffset = 0; - let curcode = -1; - let curglyph: string[] = []; - const re_prop = /^([\w-]+):\s+(.+)/i; - const re_label = /^0x([0-9a-f]+):|u[+]([0-9a-f]+):|(\w+):/i; - const re_gline = /^\s+([.@]+)/ - function addfont() { - if (curcode >= 0 && curglyph.length) { - glyphs[curcode] = decodeglyph(curglyph, curcode, yoffset); - curcode = -1; - curglyph = []; - } - } - for (let line of lines) { - let m: RegExpExecArray; - if (m = re_prop.exec(line)) { - properties[m[1]] = m[2]; - if (m[1] === 'bottom') yoffset = parseInt(m[2]); - if (m[1] === 'size') maxheight = parseInt(m[2]); - } else if (m = re_label.exec(line)) { - addfont(); - if (m[1] != null) curcode = parseInt(m[1], 16); - else if (m[2] != null) curcode = parseInt(m[2], 16); - else if (m[3] != null) curcode = null; // text labels not supported - } else if (m = re_gline.exec(line)) { - curglyph.push(m[1]); - } - if (isNaN(curcode + yoffset + maxheight)) - throw new Error(`couldn't decode .yaff: ${JSON.stringify(line)}`) - } - addfont(); - return { maxheight, properties, glyphs }; - } - // https://github.com/robhagemans/monobit - export function decodedrawlines(lines: string[]): Font { - let maxheight = 0; - let properties = {}; - let glyphs = {}; - let curcode = -1; - let curglyph: string[] = []; - const re_gline = /^([0-9a-f]+)?[:]?\s*([-#]+)/i; - function addfont() { - if (curcode >= 0 && curglyph.length) { - glyphs[curcode] = decodeglyph(curglyph, curcode, 0); - maxheight = Math.max(maxheight, curglyph.length); - curcode = -1; - curglyph = []; - } - } - for (let line of lines) { - let m: RegExpExecArray; - if (m = re_gline.exec(line)) { - if (m[1] != null) { - addfont(); - curcode = parseInt(m[1], 16); - if (isNaN(curcode)) - throw new Error(`couldn't decode .draw: ${JSON.stringify(line)}`) - } - curglyph.push(m[2]); - } - } - addfont(); - return { maxheight, properties, glyphs }; - } -} - -// TODO: merge w/ pixeleditor - -export type PixelEditorImageFormat = { - w:number - h:number - count?:number - bpp?:number - np?:number - bpw?:number - sl?:number - pofs?:number - remap?:number[] - reindex?:number[] - brev?:boolean - flip?:boolean - destfmt?:PixelEditorImageFormat - xform?:string - skip?:number - aspect?:number - }; - - function remapBits(x:number, arr:number[]) : number { - if (!arr) return x; - var y = 0; - for (var i=0; i, fmt:PixelEditorImageFormat) : Uint8Array[] { - var width = fmt.w; - var height = fmt.h; - var count = fmt.count || 1; - var bpp = fmt.bpp || 1; - var nplanes = fmt.np || 1; - var bitsperword = fmt.bpw || 8; - var wordsperline = fmt.sl || Math.ceil(width * bpp / bitsperword); - var mask = (1 << bpp)-1; - var pofs = fmt.pofs || wordsperline*height*count; - var skip = fmt.skip || 0; - var images = []; - for (var n=0; n>(bitsperword-shift-bpp) : byte>>shift) & mask) << (p*bpp); - } - imgdata.push(color); - shift += bpp; - if (shift >= bitsperword && !fmt.reindex) { - ofs0 += 1; - shift = 0; - } - } - } - images.push(new Uint8Array(imgdata)); - } - return images; - } - \ No newline at end of file diff --git a/src/common/script/lib/color.ts b/src/common/script/lib/color.ts deleted file mode 100644 index 2369d644..00000000 --- a/src/common/script/lib/color.ts +++ /dev/null @@ -1,163 +0,0 @@ - -import _chroma from 'chroma-js' -import { isArray, rgb2bgr } from '../../util'; - -export type Chroma = { _rgb: [number,number,number,number] }; - -export type ColorSource = number | [number,number,number] | [number,number,number,number] | string | Chroma; - -function checkCount(count) { - if (count < 0 || count > 65536) { - throw new Error("Palettes cannot have more than 2^16 (65536) colors."); - } -} - -export function isPalette(object): object is Palette { - return object['colors'] instanceof Uint32Array; -} - -export function isChroma(object): object is Chroma { - return object['_rgb'] instanceof Array; -} - -export class Palette { - readonly colors: Uint32Array; - - constructor(arg: number | any[] | Uint32Array) { - // TODO: more array types - if (typeof arg === 'number') { - checkCount(arg); - this.colors = new Uint32Array(arg); - } else if (arg instanceof Uint32Array) { - this.colors = new Uint32Array(arg); - } else if (isArray(arg)) { - this.colors = new Uint32Array(arg.map(rgb)); - } else - throw new Error(`Invalid Palette constructor`) - } - get(index: number) { - return this.colors[index]; - } - chromas() { - return Array.from(this.colors).map((rgba) => from(rgba & 0xffffff)); - } -} - -export const chroma = _chroma; - -export function from(obj: ColorSource) { - if (typeof obj === 'number') - return _chroma(rgb2bgr(obj & 0xffffff)); - else - return _chroma(obj as any); -} - -export function rgb(obj: ColorSource) : number; -export function rgb(r: number, g: number, b: number) : number; - -export function rgb(obj: any, g?: number, b?: number) : number { - return rgba(obj, g, b, 0xff) | 0xff000000; -} - -export function rgba(obj: ColorSource) : number; -export function rgba(r: number, g: number, b: number, a: number) : number; - -export function rgba(obj: ColorSource, g?: number, b?: number, a?: number) : number { - if (isChroma(obj)) { - return rgba(obj._rgb[0], obj._rgb[1], obj._rgb[2], obj._rgb[3]); - } - if (typeof obj === 'number') { - let r = obj; - if (typeof g === 'number' && typeof b === 'number') - return ((r & 0xff) << 0) | ((g & 0xff) << 8) | ((b & 0xff) << 16) | ((a & 0xff) << 24); - else - return obj; - } - if (typeof obj !== 'string' && isArray(obj) && typeof obj[0] === 'number') { - let arr = obj; - let v = 0; - v |= (arr[0] & 0xff) << 0; - v |= (arr[1] & 0xff) << 8; - v |= (arr[2] & 0xff) << 16; - v |= (arr[3] & 0xff) << 24; - return v; - } - return rgba(from(obj).rgb()); -} - -export function rgba2arr(v: number): number[] { - return [ - (v >> 0) & 0xff, - (v >> 8) & 0xff, - (v >> 16) & 0xff, - (v >> 24) & 0xff, - ] -} - -export function rgb2arr(v: number): number[] { - return rgba2arr(v).slice(0,3); -} - -type ColorGenFunc = (index: number) => number; - -export namespace palette { - export function from(obj: number | any[] | Uint32Array | ColorGenFunc, count?: number) { - checkCount(count); - if (typeof obj === 'function') { - if (!count) throw new Error(`You must also pass the number of colors to generate.`) - var pal = new Palette(count); - for (var i = 0; i < pal.colors.length; i++) { - pal.colors[i] = rgba(obj(i)); - } - return pal; - } else { - return new Palette(obj); - } - } - export function mono() { - return greys(2); - } - function rgb2() { - return new Palette([ - rgb(0, 0, 0), - rgb(0, 0, 255), - rgb(255, 0, 0), - rgb(0, 255, 0), - ]); - } - function rgb3() { - return new Palette([ - rgb(0, 0, 0), - rgb(0, 0, 255), - rgb(255, 0, 0), - rgb(255, 0, 255), - rgb(0, 255, 0), - rgb(0, 255, 255), - rgb(255, 255, 0), - rgb(255, 255, 255), - ]); - } - export function greys(count: number) { - return from((i) => { - let v = 255 * i / (count - 1); - return rgb(v,v,v); - }, count); - } - export function colors(count: number) { - switch (count) { - case 2: return mono(); - case 4: return rgb2(); - case 8: return rgb3(); - default: return factors(count); // TODO - } - } - export function helix(count: number) { - checkCount(count); - return new Palette(chroma.cubehelix().scale().colors(count)); - } - export function factors(count: number, mult?: number) { - mult = mult || 0x031f0f; - return from((i) => rgb(i * mult), count); - } - // TODO: https://www.iquilezles.org/www/articles/palettes/palettes.htm -} diff --git a/src/common/script/lib/io.ts b/src/common/script/lib/io.ts deleted file mode 100644 index 2c1a2fe4..00000000 --- a/src/common/script/lib/io.ts +++ /dev/null @@ -1,178 +0,0 @@ - -import { FileDataCache } from "../../util"; -import { FileData, WorkingStore } from "../../workertypes"; - -// remote resource cache -var $$cache = new FileDataCache(); // TODO: better cache? -// file read/write interface -var $$store: WorkingStore; -// backing store for data -var $$data: {} = {}; -// module cache -var $$modules: Map = new Map(); - -export function $$setupFS(store: WorkingStore) { - $$store = store; -} -export function $$getData() { - return $$data; -} -export function $$loadData(data: {}) { - Object.assign($$data, data); -} - -// object that can load state from backing store -export interface Loadable { - // called during script, from io.data.load() - $$setstate?(newstate: {}) : void; - // called after script, from io.data.save() - $$getstate() : {}; -} - -export namespace data { - export function load(object: Loadable, key: string): Loadable { - if (object == null) return object; - let override = $$data && $$data[key]; - if (override && object.$$setstate) { - object.$$setstate(override); - } else if (override) { - Object.assign(object, override); - } - return object; - } - export function save(object: Loadable, key: string): Loadable { - if ($$data && object && object.$$getstate) { - $$data[key] = object.$$getstate(); - } - return object; - } - export function get(key: string) { - return $$data && $$data[key]; - } - export function set(key: string, value: object) { - if ($$data) { - $$data[key] = value; - } - } -} - -export class IOWaitError extends Error { -} - -export function canonicalurl(url: string) : string { - // get raw resource URL for github - if (url.startsWith('https://github.com/')) { - let toks = url.split('/'); - if (toks[5] === 'blob') { - return `https://raw.githubusercontent.com/${toks[3]}/${toks[4]}/${toks.slice(6).join('/')}` - } - } - return url; -} - -export function fetchurl(url: string, type?: 'binary' | 'text'): FileData { - // TODO: only works in web worker - var xhr = new XMLHttpRequest(); - xhr.responseType = type === 'text' ? 'text' : 'arraybuffer'; - xhr.open("GET", url, false); // synchronous request - xhr.send(null); - if (xhr.response != null && xhr.status == 200) { - if (type === 'text') { - return xhr.response as string; - } else { - return new Uint8Array(xhr.response); - } - } else { - throw new Error(`The resource at "${url}" responded with status code of ${xhr.status}.`) - } -} - -export function readnocache(url: string, type?: 'binary' | 'text'): FileData { - if (url.startsWith('http:') || url.startsWith('https:')) { - return fetchurl(url, type); - } - if ($$store) { - return $$store.getFileData(url); - } -} - -// TODO: read files too -export function read(url: string, type?: 'binary' | 'text'): FileData { - // canonical-ize url - url = canonicalurl(url); - // check cache first - let cachekey = url; - let data = $$cache.get(cachekey); - if (data != null) return data; - // not in cache, read it - data = readnocache(url, type); - if (data == null) throw new Error(`Cannot find resource "${url}"`); - if (type === 'text' && typeof data !== 'string') throw new Error(`Resource "${url}" is not a string`); - if (type === 'binary' && !(data instanceof Uint8Array)) throw new Error(`Resource "${url}" is not a binary file`); - // store in cache - $$cache.put(cachekey, data); - return data; -} - -export function readbin(url: string): Uint8Array { - var data = read(url, 'binary'); - if (data instanceof Uint8Array) - return data; - else - throw new Error(`The resource at "${url}" is not a binary file.`); -} - -export function readlines(url: string) : string[] { - return (read(url, 'text') as string).split('\n'); -} - -export function splitlines(text: string) : string[] { - return text.split(/\n|\r\n/g); -} - -export function module(url: string) { - // find module in cache? - let key = `${url}::${url.length}`; - let exports = $$modules.get(key); - if (exports == null) { - let code = readnocache(url, 'text') as string; - let func = new Function('exports', 'module', code); - let module = {}; // TODO? - exports = {}; - func(exports, module); - $$modules.set(key, exports); - } - return exports; -} - -/// - -// TODO: what if this isn't top level? -export class Mutable implements Loadable { - value : T; - constructor(initial : T) { - this.value = initial; - } - $$setstate(newstate) { - this.value = newstate.value; - } - $$getstate() { - return { value: this.value }; - } -} - -export function mutable(obj: object) : object { - Object.defineProperty(obj, '$$setstate', { - value: function(newstate) { - Object.assign(this, newstate); - }, - enumerable: false - }); - Object.defineProperty(obj, '$$getstate', { - value: function() { - return this; - }, - enumerable: false - }); - return obj; -} diff --git a/src/common/script/lib/output.ts b/src/common/script/lib/output.ts deleted file mode 100644 index 0d6bc7bb..00000000 --- a/src/common/script/lib/output.ts +++ /dev/null @@ -1,85 +0,0 @@ - -enum DataType { - unknown, - u8, - s8, - u16, - s16, - u32, - s32, - f32, - f64, -}; - -function getArrayDataType(value: any) : DataType { - if (value instanceof Uint8Array) { - return DataType.u8; - } else if (value instanceof Int8Array) { - return DataType.s8; - } else if (value instanceof Uint16Array) { - return DataType.u16; - } else if (value instanceof Int16Array) { - return DataType.s16; - } else if (value instanceof Uint32Array) { - return DataType.u32; - } else if (value instanceof Int32Array) { - return DataType.s32; - } else if (value instanceof Float32Array) { - return DataType.f32; - } else if (value instanceof Float64Array) { - return DataType.f64; - } -} - -export abstract class OutputFile { - constructor( - public readonly path : string, - public readonly decls : {} - ) { - } - abstract declToText(label: string, value: any) : string; - toString() : string { - return Object.entries(this.decls).map(entry => this.declToText(entry[0],entry[1])).join('\n\n'); - } -} - -export class COutputFile extends OutputFile { - toString() : string { - return `#include \n\n${super.toString()}\n`; - } - dataTypeToString(dtype: DataType) { - switch (dtype) { - case DataType.u8: return 'uint8_t'; - case DataType.s8: return 'int8_t'; - case DataType.u16: return 'uint16_t'; - case DataType.s16: return 'int16_t'; - case DataType.u32: return 'uint32_t'; - case DataType.s32: return 'int32_t'; - case DataType.f32: return 'float'; - case DataType.f64: return 'double'; - default: - throw new Error('Cannot convert data type'); // TODO - } - } - valueToString(value, atype: DataType) : string { - // TODO: round, check value - return value+""; - } - declToText(label: string, value: any) : string { - if (Array.isArray(value) || value['BYTES_PER_ELEMENT']) { - let atype = getArrayDataType(value); - if (atype != null) { - let dtypestr = this.dataTypeToString(atype); - let dtext = value.map(elem => this.valueToString(elem, atype)).join(','); - let len = value.length; - return `${dtypestr} ${label}[${len}] = { ${dtext} };`; - } - } - throw new Error(`Cannot convert array "${label}"`); // TODO - } -} - -// TODO: header file, auto-detect tool? -export function file(path: string, decls: {}) { - return new COutputFile(path, decls); -} diff --git a/src/common/script/lib/scriptui.ts b/src/common/script/lib/scriptui.ts deleted file mode 100644 index 4f825cfc..00000000 --- a/src/common/script/lib/scriptui.ts +++ /dev/null @@ -1,193 +0,0 @@ - -import * as io from "./io"; - -// sequence counter -var $$seq : number = 0; - -// if an event is specified, it goes here -export const EVENT_KEY = "$$event"; - -// an object that can become interactive, identified by ID -export interface Interactive { - $$interact: InteractionRecord; -} - -export interface InteractEvent { - interactid : number; - type: string; - x?: number; - y?: number; - button?: boolean; -} - -export type InteractCallback = (event: InteractEvent) => void; - -// InteractionRecord maps a target object to an interaction ID -// the $$callback is used once per script eval, then gets nulled -// whether or not it's invoked -// event comes from $$data.$$event -export class InteractionRecord implements io.Loadable { - readonly interacttarget: Interactive; - interactid : number; - lastevent : {} = null; - constructor( - interacttarget: Interactive, - private $$callback: InteractCallback - ) { - this.interacttarget = interacttarget || (this as Interactive); - this.interactid = ++$$seq; - } - $$setstate(newstate: {interactid: number}) { - this.interactid = newstate.interactid; - this.interacttarget.$$interact = this; - let event : InteractEvent = io.data.get(EVENT_KEY); - if (event && event.interactid == this.interactid) { - if (this.$$callback) { - this.$$callback(event); - } - this.lastevent = event; - io.data.set(EVENT_KEY, null); - } - this.$$callback = null; - } - $$getstate() { - //TODO: this isn't always cleared before we serialize (e.g. if exception or move element) - //and we do it in checkResult() too - this.$$callback = null; - return {interactid: this.interactid}; - } -} - -export function isInteractive(obj: object): obj is Interactive { - return !!((obj as Interactive).$$interact); -} - -export function interact(object: any, callback) : InteractionRecord { - // TODO: limit to Bitmap, etc - if (typeof object === 'object') { - return new InteractionRecord(object, callback); - } - throw new Error(`This object is not capable of interaction.`); -} - -/// - -export interface ScriptUIType { - uitype : string; -} - -export class ScriptUISliderType implements ScriptUIType { - readonly uitype = 'slider'; - value: number; - constructor( - readonly min: number, - readonly max: number, - readonly step: number - ) { - this.value = min; - } -} - -export class ScriptUISlider extends ScriptUISliderType implements io.Loadable { - initial(value: number) { - this.value = value; - return this; - } - $$getstate() { - return { value: this.value }; - } -} - -export function slider(min: number, max: number, step?: number) { - return new ScriptUISlider(min, max, step || 1); -} - -/// - -export class ScriptUISelectType implements ScriptUIType { - readonly uitype = 'select'; - value: T; - index: number; - constructor( - readonly options: T[] - ) { - this.index = 0; - this.value = this.options[this.index]; - } -} - -export class ScriptUISelect extends ScriptUISelectType implements io.Loadable { - initial(index: number) { - this.index = index; - this.value = this.options[index]; - return this; - } - $$getstate() { - return { value: this.value, index: this.index }; - } -} - -export function select(options: any[]) { - return new ScriptUISelect(options); -} - -/// - -export class ScriptUIButtonType extends InteractionRecord implements ScriptUIType, Interactive { - readonly uitype = 'button'; - $$interact: InteractionRecord; - enabled?: boolean; - - constructor( - readonly label: string, - callback: InteractCallback - ) { - super(null, callback); - this.$$interact = this; - } -} - -export class ScriptUIButton extends ScriptUIButtonType { -} - -export function button(name: string, callback: InteractCallback) { - return new ScriptUIButton(name, callback); -} - -export class ScriptUIToggle extends ScriptUIButton implements io.Loadable { - // share with InteractionRecord - $$getstate() { - let state = super.$$getstate() as any; - state.enabled = this.enabled; - return state; - } - $$setstate(newstate: any) { - this.enabled = newstate.enabled; - super.$$setstate(newstate); - } -} - -export function toggle(name: string) { - return new ScriptUIToggle(name, function(e) { - this.enabled = !this.enabled; - }); -} - -/// - -export class ScriptUIShortcut extends InteractionRecord implements ScriptUIType, Interactive { - readonly uitype = 'shortcut'; - $$interact: InteractionRecord; - - constructor( - readonly key: string, - callback: InteractCallback - ) { - super(null, callback); - this.$$interact = this; - } -} - -export function key(key: string, callback: InteractCallback) { - return new ScriptUIShortcut(key, callback); -} diff --git a/src/common/script/ui/notebook.ts b/src/common/script/ui/notebook.ts deleted file mode 100644 index 79cb3529..00000000 --- a/src/common/script/ui/notebook.ts +++ /dev/null @@ -1,479 +0,0 @@ - -import { Component, render, h, createRef, VNode } from 'preact'; -import { Cell, PROP_CONSTRUCTOR_NAME } from "../env"; -import { findIntegerFactors, hex, isArray, rgb2bgr } from "../../util"; -import { dumpRAM } from "../../emu"; -import { current_project } from "../../../ide/ui"; -// TODO: can't call methods from this end (e.g. Palette, Bitmap) -import * as bitmap from "../lib/bitmap"; -import * as color from "../lib/color"; -import * as scriptui from "../lib/scriptui"; - -const MAX_STRING_LEN = 100; -const DEFAULT_ASPECT = 1; - -function sendInteraction(iobj: scriptui.Interactive, type: string, event: Event, xtraprops: {}) { - let irec = iobj.$$interact; - let ievent : scriptui.InteractEvent = {interactid: irec.interactid, type, ...xtraprops}; - if (event instanceof PointerEvent) { - const canvas = event.target as HTMLCanvasElement; - const rect = canvas.getBoundingClientRect(); - const scaleX = canvas.width / rect.width; - const scaleY = canvas.height / rect.height; - const x = (event.clientX - rect.left) * scaleX; - const y = (event.clientY - rect.top) * scaleY; - ievent.x = Math.floor(x); - ievent.y = Math.floor(y); - // TODO: pressure, etc. - } else { - console.log("Unknown event type", event); - } - // TODO: add events to queue? - current_project.updateDataItems([{ - key: scriptui.EVENT_KEY, - value: ievent - }]); -} - -interface ColorComponentProps { - rgbavalue: number; -} - -class ColorComponent extends Component { - render(virtualDom, containerNode, replaceNode) { - let rgb = this.props.rgbavalue & 0xffffff; - let bgr = rgb2bgr(rgb); - var htmlcolor = `#${hex(bgr,6)}`; - var textcolor = (rgb & 0x008000) ? '#222' : '#ddd'; - var printcolor = hex(rgb & 0xffffff, 6); // TODO: show index instead? - return h('div', { - class: 'scripting-item scripting-color', - style: `background-color: ${htmlcolor}; color: ${textcolor}`, - alt: htmlcolor, // TODO - }, [ - //h('span', { }, printcolor ) - ]); - } -} - -interface BitmapComponentProps { - bitmap: bitmap.BitmapType; - width: number; - height: number; -} - -class BitmapComponent extends Component { - ref = createRef(); // TODO: can we use the ref? - canvas: HTMLCanvasElement; - ctx: CanvasRenderingContext2D; - imageData: ImageData; - datau32: Uint32Array; - pressed = false; - - constructor(props: BitmapComponentProps) { - super(props); - } - render(virtualDom, containerNode, replaceNode) { - let props = { - class: 'scripting-item', - ref: this.ref, - width: this.props.width, - height: this.props.height, - style: this.props.bitmap.style - } - let obj : any = this.props.bitmap; - if (scriptui.isInteractive(obj)) { - return h('canvas', { - onPointerMove: (e: PointerEvent) => { - sendInteraction(obj, 'move', e, { pressed: this.pressed }); - }, - onPointerDown: (e: PointerEvent) => { - this.pressed = true; - this.canvas.setPointerCapture(e.pointerId); - sendInteraction(obj, 'down', e, { pressed: true }); - }, - onPointerUp: (e: PointerEvent) => { - this.pressed = false; - sendInteraction(obj, 'up', e, { pressed: false }); - }, - onPointerOut: (e: PointerEvent) => { - this.pressed = false; - sendInteraction(obj, 'out', e, { pressed: false }); - }, - ...props - }); - } else { - return h('canvas', props); - } - } - componentDidMount() { - this.refresh(); - } - componentWillUnmount() { - this.canvas = null; - this.imageData = null; - this.datau32 = null; - } - componentDidUpdate(prevProps, prevState, snapshot) { - this.refresh(); - } - prepare() { - this.canvas = this.base as HTMLCanvasElement; - this.ctx = this.canvas.getContext('2d'); - this.imageData = this.ctx.createImageData(this.canvas.width, this.canvas.height); - this.datau32 = new Uint32Array(this.imageData.data.buffer); - } - refresh() { - // preact can reuse this component but it can change shape :^P - if (this.canvas !== this.base - || this.imageData.width != this.props.width - || this.imageData.height != this.props.height) { - this.prepare(); - } - this.updateCanvas(this.datau32, this.props.bitmap); - this.ctx.putImageData(this.imageData, 0, 0); - } - updateCanvas(vdata: Uint32Array, bmp: bitmap.BitmapType) { - if (bmp['palette']) { - this.updateCanvasIndexed(vdata, bmp as bitmap.IndexedBitmap); - } - if (bmp['rgba']) { - this.updateCanvasRGBA(vdata, bmp as bitmap.RGBABitmap); - } - } - updateCanvasRGBA(vdata: Uint32Array, bmp: bitmap.RGBABitmap) { - vdata.set(bmp.rgba); - } - updateCanvasIndexed(vdata: Uint32Array, bmp: bitmap.IndexedBitmap) { - let pal = bmp.palette.colors; - for (var i = 0; i < bmp.pixels.length; i++) { - vdata[i] = pal[bmp.pixels[i]]; - } - } -} - -interface ObjectTreeComponentProps { - name?: string; - object: {} | []; - objpath: string; -} - -interface ObjectTreeComponentState { - expanded: boolean; -} - -class ObjectKeyValueComponent extends Component { - render(virtualDom, containerNode, replaceNode) { - let expandable = typeof this.props.object === 'object'; - let hdrclass = ''; - if (expandable) hdrclass = this.state.expanded ? 'tree-expanded' : 'tree-collapsed' - let propName = this.props.name || null; - return h('div', { - class: 'tree-content', - key: `${this.props.objpath}__tree` - }, [ - h('div', { - class: 'tree-header ' + hdrclass, - onClick: expandable ? () => this.toggleExpand() : null - }, [ - propName != null ? h('span', { class: 'tree-key' }, [ propName, expandable ]) : null, - h('span', { class: 'tree-value scripting-item' }, [ - getShortName(this.props.object) - ]) - ]), - this.state.expanded ? objectToContentsDiv(this.props.object, this.props.objpath) : null - ]); - } - toggleExpand() { - this.setState({ expanded: !this.state.expanded }); - } -} - -function getShortName(object: any) { - if (typeof object === 'object') { - try { - var s = object[PROP_CONSTRUCTOR_NAME] || Object.getPrototypeOf(object).constructor.name; - if (object.length > 0) { - s += `[${object.length}]` - } - return s; - } catch (e) { - console.log(e); - return e + ""; - } - } else { - return primitiveToString(object); - } -} - -function primitiveToString(obj) { - var text = ""; - // is it a function? call it first, if we are expanded - // TODO: only call functions w/ signature - if (obj && obj.$$ && typeof obj.$$ == 'function' && this._content != null) { - obj = obj.$$(); - } - // check null first - if (obj == null) { - text = obj + ""; - // primitive types - } else if (typeof obj == 'number') { - if (obj != (obj | 0)) text = obj.toString(); // must be a float - else text = obj + "\t($" + hex(obj) + ")"; - } else if (typeof obj == 'string') { - text = obj; - } else { - text = JSON.stringify(obj); - } - if (text.length > MAX_STRING_LEN) - text = text.substring(0, MAX_STRING_LEN) + "..."; - return text; -} - -function isIndexedBitmap(object): object is bitmap.IndexedBitmap { - return object['bpp'] && object['pixels'] && object['palette']; -} -function isRGBABitmap(object): object is bitmap.RGBABitmap { - return object['rgba'] instanceof Uint32Array; -} - -function objectToChildren(object: any) : any[] { - if (color.isPalette(object)) { - return new color.Palette(object.colors).chromas(); - } else if (isArray(object)) { - return Array.from(object); - } else if (object != null) { - return [ object ] - } else { - return [ ] - } -} - -function objectToChild(object: any, index: number) : any { - if (color.isPalette(object)) { - return color.from(object.colors[index]); - } else if (isArray(object)) { - return object[index]; - } else if (object != null) { - return object - } else { - return null - } -} - -function objectToDiv(object: any, name: string, objpath: string): VNode { - // don't view any keys that start with "$" - if (name && name.startsWith("$")) { - // don't view any values that start with "$$" - if (name.startsWith("$$")) return; - // don't print if null or undefined - if (object == null) return; - // don't print key in any case - name = null; - } - // TODO: limit # of items - // TODO: detect table - if (object == null) { - return h('span', { }, object + ""); - } else if (object['uitype']) { - let cons = UI_COMPONENTS[object['uitype']]; - if (!cons) throw new Error(`Unknown UI component type: ${object['uitype']}`); - return h(cons, { iokey: objpath, uiobject: object }); - } else if (object['literaltext']) { - return h("pre", { }, [ object['literaltext'] ]); - } else if (isIndexedBitmap(object) || isRGBABitmap(object)) { - return h(BitmapComponent, { bitmap: object, width: object.width, height: object.height }); - } else if (color.isChroma(object)) { - return h(ColorComponent, { rgbavalue: color.rgb(object) }); - } else if (color.isPalette(object)) { - // TODO? - if (object.colors.length <= 256) { - let children = []; - let props = { class: '', key: `${objpath}__obj` }; - props.class += ' scripting-flex '; - object.colors.forEach((val) => { - children.push(h(ColorComponent, { rgbavalue: val })); - }) - return h('div', props, children); - } else { - let {a,b} = findIntegerFactors(object.colors.length, 1, 1, DEFAULT_ASPECT); - return objectToDiv({ rgba: object.colors, width: a, height: b }, name, objpath); - } - } else { - return h(ObjectKeyValueComponent, { name, object, objpath }, []); - } -} - -function fixedArrayToDiv(tyarr: Array, bpel: number, objpath: string) { - const maxBytes = 0x100; - if (tyarr.length <= maxBytes) { - // TODO - let dumptext = dumpRAM(tyarr, 0, tyarr.length); - return h('pre', {}, dumptext); - } else { - let children = []; - for (var ofs = 0; ofs < tyarr.length; ofs += maxBytes) { - children.push(objectToDiv(tyarr.slice(ofs, ofs + maxBytes), '$' + hex(ofs), `${objpath}.${ofs}`)); - } - return h('div', {}, children); - } -} - -function objectToContentsDiv(object: {} | [], objpath: string) { - // is typed array? - let bpel = object['BYTES_PER_ELEMENT']; - if (typeof bpel === 'number') { - return fixedArrayToDiv(object as Array, bpel, objpath); - } - let objectEntries = Object.entries(object); - let objectDivs = objectEntries.map(entry => objectToDiv(entry[1], entry[0], `${objpath}.${entry[1]}`)); - return h('div', { class: 'scripting-flex' }, objectDivs); -} - -/// - -interface UIComponentProps { - iokey: string; - uiobject: scriptui.ScriptUIType; - dropdown?: boolean; -} - -class UISliderComponent extends Component { - render(virtualDom, containerNode, replaceNode) { - let slider = this.props.uiobject as scriptui.ScriptUISliderType; - return h('div', {}, [ - this.props.iokey, - h('input', { - type: 'range', - min: slider.min / slider.step, - max: slider.max / slider.step, - value: slider.value / slider.step, - onInput: (ev) => { - let target = ev.target as HTMLInputElement; // TODO - let newUIValue = { value: parseFloat(target.value) * slider.step }; - this.setState(this.state); - current_project.updateDataItems([{key: this.props.iokey, value: newUIValue}]); - } - }), - h('span', { }, getShortName(slider.value)), - ]); - } -} - -class UISelectComponent extends Component { - ref = createRef(); - render(virtualDom, containerNode, replaceNode) { - let select = this.props.uiobject as scriptui.ScriptUISelectType; - let children = objectToChildren(select.options); - this.props.dropdown = children.length > 16; - let showselections = !this.props.dropdown || this.state.expanded; - let seldiv = null; - if (showselections) { - seldiv = h('div', { - class: 'scripting-select scripting-flex', - ref: this.ref, - onClick: (e) => { - // select object -- iterate parents until we find select div, then find index of child - let target = e.target as HTMLElement; - while (target.parentElement && target.parentElement != this.ref.current) { - target = target.parentElement; - } - if (target.parentElement) { - const selindex = Array.from(target.parentElement.children).indexOf(target); - if (selindex >= 0 && selindex < children.length) { - let newUIValue = { value: children[selindex], index: selindex }; - this.setState({ expanded: false }); - current_project.updateDataItems([{key: this.props.iokey, value: newUIValue}]); - } else { - throw new Error(`Could not find click target of ${this.props.iokey}`); - } - } - } - }, - children.map((child, index) => { - let div = objectToDiv(child, null, `${this.props.iokey}__select_${index}`); - let selected = (index == select.index); - return h('div', { class: selected ? 'scripting-selected' : '' }, [ div ]); - })); - } - if (this.props.dropdown) { - let selectedDiv = objectToDiv(objectToChild(select.options, select.index), null, `${this.props.iokey}__selected`); - return h('div', { - class: 'tree-content', - key: `${this.props.iokey}__tree` - }, [ - h('div', { - class: 'tree-header ' + (this.state.expanded ? 'tree-expanded' : 'tree-collapsed'), - onClick: () => this.toggleExpand() - }, [ - this.props.iokey, - h('span', { class: 'tree-value scripting-item' }, [ - selectedDiv, - //getShortName(select.options) - ]) - ]), - seldiv - ]); - } else { - return seldiv; - } - } - toggleExpand() { - this.setState({ expanded: !this.state.expanded }); - } -} - -class UIButtonComponent extends Component { - render(virtualDom, containerNode, replaceNode) { - let button = this.props.uiobject as scriptui.ScriptUIButtonType; - return h('button', { - class: button.enabled ? 'scripting-button scripting-enabled' : 'scripting-button', - onClick: (e: MouseEvent) => { - sendInteraction(button, 'click', e, { }); - }, - }, [ - button.label - ]) - } -} - -class UIShortcutComponent extends Component { - render(virtualDom, containerNode, replaceNode) { - let shortcut = this.props.uiobject as scriptui.ScriptUIShortcut; - // TODO: needs to fire on container node - return h('div', { - onKeyDown: (e: KeyboardEvent) => { - sendInteraction(shortcut, 'key', e, { }); - }, - }, [ ]) - } -} - -const UI_COMPONENTS = { - 'slider': UISliderComponent, - 'select': UISelectComponent, - 'button': UIButtonComponent, - 'shortcut': UIShortcutComponent, -} - -/// - -export class Notebook { - constructor( - public readonly maindoc: HTMLDocument, - public readonly maindiv: HTMLElement - ) { - maindiv.classList.add('vertical-scroll'); - } - updateCells(cells: Cell[]) { - let hTree = cells.map(cell => { - return h('div', { - class: 'scripting-cell', - key: `${cell.id}__cell` - }, [ - objectToDiv(cell.object, cell.id, cell.id) - ]) - }); - render(hTree, this.maindiv); - } -} diff --git a/src/ide/project.ts b/src/ide/project.ts index 1ad0e5c3..f2d8326c 100644 --- a/src/ide/project.ts +++ b/src/ide/project.ts @@ -165,13 +165,7 @@ export class CodeProject { parseIncludeDependencies(text:string):string[] { let files = []; let m; - if (this.platform_id.startsWith('script')) { // TODO - let re1 = /\b\w+[.]read\(["'](.+?)["']/gmi; - while (m = re1.exec(text)) { - if (m[1] && m[1].indexOf(':/') < 0) // TODO: ignore URLs - this.pushAllFiles(files, m[1]); - } - } else if (this.platform_id.startsWith('verilog')) { + if (this.platform_id.startsWith('verilog')) { // include verilog includes let re1 = /^\s*(`include|[.]include)\s+"(.+?)"/gmi; while (m = re1.exec(text)) { diff --git a/src/machine/williams.ts b/src/machine/williams.ts index e53ea202..4ced1f3c 100644 --- a/src/machine/williams.ts +++ b/src/machine/williams.ts @@ -1,4 +1,3 @@ -import { hex } from "chroma-js"; import { MasterAudio, WorkerSoundChannel } from "../common/audio"; import { MemoryBus } from "../common/baseplatform"; import { CPU6809 } from "../common/cpu/6809"; diff --git a/src/platform/_index.ts b/src/platform/_index.ts index e71fedf4..ccea0030 100644 --- a/src/platform/_index.ts +++ b/src/platform/_index.ts @@ -19,7 +19,6 @@ export function importPlatform(name: string) : Promise { case "mw8080bw": return import("../platform/mw8080bw"); case "nes": return import("../platform/nes"); case "pce": return import("../platform/pce"); - case "script": return import("../platform/script"); case "sms": return import("../platform/sms"); case "sound_konami": return import("../platform/sound_konami"); case "sound_williams": return import("../platform/sound_williams"); diff --git a/src/platform/script.ts b/src/platform/script.ts deleted file mode 100644 index 0664664b..00000000 --- a/src/platform/script.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import { PLATFORMS } from "../common/emu"; -import { Platform } from "../common/baseplatform"; -import { RunResult } from "../common/script/env"; -import { Notebook } from "../common/script/ui/notebook"; - -class ScriptingPlatform implements Platform { - mainElement: HTMLElement; - iframe: HTMLIFrameElement; - notebook: Notebook; - - constructor(mainElement: HTMLElement) { - this.mainElement = mainElement; - this.notebook = new Notebook(document, mainElement); - } - start() { - } - reset() { - } - pause() { - } - resume() { - } - loadROM(title, run: RunResult) { - this.notebook.updateCells(run.cells); - // TODO: save state file - } - isRunning() { - return true; - } - isDebugging(): boolean { - return false; - } - getToolForFilename(fn: string): string { - return "js"; - } - getDefaultExtension(): string { - return ".js"; - } - getPresets() { - return [ - ]; - } -} - -PLATFORMS['script'] = ScriptingPlatform; diff --git a/src/worker/tools/script.ts b/src/worker/tools/script.ts deleted file mode 100644 index 4d5f533b..00000000 --- a/src/worker/tools/script.ts +++ /dev/null @@ -1,46 +0,0 @@ - -import { BuildStep, BuildStepResult, emglobal, store } from "../workermain"; -import { Environment, RunResult } from "../../common/script/env"; -import * as io from "../../common/script/lib/io"; -import { createNewPersistentStore } from "../../ide/project"; - -// cache environments -var environments: { [path: string]: Environment } = {}; - -function getEnv(path: string): Environment { - var env = environments[path]; - if (!env) { - env = environments[path] = new Environment(emglobal, path); - // TODO: load environment from store? - } - return env; -} - -export async function runJavascript(step: BuildStep): Promise { - var env = getEnv(step.path); - var code = store.getFileAsString(step.path); - var lstore = createNewPersistentStore(step.platform + "//items"); - // load items from persistent storage (TODO) - const itemskey = step.path; - if (store.items == null) { - store.items = (await lstore.getItem(itemskey)) || {}; // TODO - console.log(store.items); - } - io.$$setupFS(store); - io.$$loadData(store.items); - try { - await env.run(code); - let cells = env.render(); - let state = env.commitLoadableState(); // TODO: doesn't work - let output : RunResult = { cells, state }; - // save items to persistent storage (TODO) - lstore.setItem(itemskey, state); // TODO - store.items = state; // TODO: why???? - return { output: output }; - } catch (e) { - console.log(e); - return { errors: env.extractErrors(e) }; - } finally { - io.$$setupFS(null); - } -} diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index eb7b2bff..fe7ed216 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -1133,7 +1133,6 @@ import * as m6502 from './tools/m6502' import * as z80 from './tools/z80' import * as x86 from './tools/x86' import * as arm from './tools/arm' -import * as script from './tools/script' import * as ecs from './tools/ecs' import * as remote from './tools/remote' @@ -1171,7 +1170,6 @@ var TOOLS = { 'wiz': misc.compileWiz, 'armips': arm.assembleARMIPS, 'vasmarm': arm.assembleVASMARM, - //'js': script.runJavascript, 'ecs': ecs.assembleECS, 'remote': remote.buildRemote }