mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-26 10:49:17 +00:00
starting on js scripting language; worker msgs can run async functions (but we don't need to ... yet)
This commit is contained in:
parent
bd00d98b77
commit
a8b2b7c043
25
css/ui.css
25
css/ui.css
@ -768,3 +768,28 @@ div.asset_toolbar {
|
|||||||
.waverow.editable:hover {
|
.waverow.editable:hover {
|
||||||
background-color: #336633;
|
background-color: #336633;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scripting-cell {
|
||||||
|
background: #444;
|
||||||
|
color: #99cc99;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0.5em;
|
||||||
|
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
.scripting-cell canvas {
|
||||||
|
height: 15vw;
|
||||||
|
border: 2px solid #222;
|
||||||
|
outline-color: #ccc;
|
||||||
|
background: #000;
|
||||||
|
padding: 6px;
|
||||||
|
margin: 6px;
|
||||||
|
pointer-events:auto;
|
||||||
|
}
|
||||||
|
.scripting-cell canvas:focus {
|
||||||
|
outline:none;
|
||||||
|
border-color:#888;
|
||||||
|
}
|
||||||
|
.scripting-cell div {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
164
package-lock.json
generated
164
package-lock.json
generated
@ -13,13 +13,16 @@
|
|||||||
"@wasmer/wasmfs": "^0.12.0",
|
"@wasmer/wasmfs": "^0.12.0",
|
||||||
"binaryen": "^101.0.0",
|
"binaryen": "^101.0.0",
|
||||||
"clipboard": "^2.0.6",
|
"clipboard": "^2.0.6",
|
||||||
|
"error-stack-parser": "^2.0.6",
|
||||||
|
"fast-png": "^5.0.4",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"jszip": "^3.7.0",
|
"jszip": "^3.7.0",
|
||||||
"localforage": "^1.9.0",
|
"localforage": "^1.9.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"octokat": "^0.10.0",
|
"octokat": "^0.10.0",
|
||||||
"split.js": "^1.6.2"
|
"split.js": "^1.6.2",
|
||||||
|
"yufka": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bootbox": "^5.1.3",
|
"@types/bootbox": "^5.1.3",
|
||||||
@ -40,7 +43,6 @@
|
|||||||
"lzg": "^1.0.x",
|
"lzg": "^1.0.x",
|
||||||
"mocha": "^7.2.0",
|
"mocha": "^7.2.0",
|
||||||
"mocha-simple-html-reporter": "^2.0.0",
|
"mocha-simple-html-reporter": "^2.0.0",
|
||||||
"pngjs": "^3.4.0",
|
|
||||||
"typedoc": "^0.21.0",
|
"typedoc": "^0.21.0",
|
||||||
"typescript": "^4.3.4",
|
"typescript": "^4.3.4",
|
||||||
"typescript-formatter": "^7.2.2"
|
"typescript-formatter": "^7.2.2"
|
||||||
@ -820,6 +822,11 @@
|
|||||||
"integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==",
|
"integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pako": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-8UJl2MjkqqS6ncpLZqRZ5LmGiFBkbYxocD4e4jmBqGvfRG1RS23gKsBQbdtV9O9GvRyjFTiRHRByjSlKCLlmZw=="
|
||||||
|
},
|
||||||
"node_modules/@types/plist": {
|
"node_modules/@types/plist": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz",
|
||||||
@ -3180,6 +3187,14 @@
|
|||||||
"is-arrayish": "^0.2.1"
|
"is-arrayish": "^0.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/error-stack-parser": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"stackframe": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-abstract": {
|
"node_modules/es-abstract": {
|
||||||
"version": "1.18.5",
|
"version": "1.18.5",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz",
|
||||||
@ -3419,6 +3434,21 @@
|
|||||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-png": {
|
||||||
|
"version": "5.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-5.0.4.tgz",
|
||||||
|
"integrity": "sha512-vTNj6yixRnclW6sTlCeH6sNRLBOhM5ITmlo1LSU5ojKEc2e9kZkqXPo2xzBxKb61MBCXRXBcr8qJztOHr2O6WQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/pako": "^1.0.1",
|
||||||
|
"iobuffer": "^5.0.2",
|
||||||
|
"pako": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-png/node_modules/pako": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg=="
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz",
|
||||||
@ -4503,6 +4533,11 @@
|
|||||||
"integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==",
|
"integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/iobuffer": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-0SNk4hbHVXx9oE27vTJY+oiI0txkhBdQV12RvILd/7XuIhBZ0TkImq5EnhFYCcRcDff8jpFhZ9C2Sg+NIo3ZMQ=="
|
||||||
|
},
|
||||||
"node_modules/ip": {
|
"node_modules/ip": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
||||||
@ -5890,6 +5925,14 @@
|
|||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.25.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||||
|
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"sourcemap-codec": "^1.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/make-dir": {
|
"node_modules/make-dir": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
@ -7699,15 +7742,6 @@
|
|||||||
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
|
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/pngjs": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
|
||||||
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||||
@ -8614,6 +8648,11 @@
|
|||||||
"source-map": "^0.6.0"
|
"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=="
|
||||||
|
},
|
||||||
"node_modules/spawn-wrap": {
|
"node_modules/spawn-wrap": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz",
|
||||||
@ -8720,6 +8759,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stackframe": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA=="
|
||||||
|
},
|
||||||
"node_modules/stat-mode": {
|
"node_modules/stat-mode": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
|
||||||
@ -10061,6 +10105,29 @@
|
|||||||
"buffer-crc32": "~0.2.3",
|
"buffer-crc32": "~0.2.3",
|
||||||
"fd-slicer": "~1.1.0"
|
"fd-slicer": "~1.1.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yufka": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yufka/-/yufka-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-VdqiJocmYfhx/yPiLFmF6ZyxKE2bzaHbocO0Y27sC5ytSQ09CLhTWP8GJAZMhGq2UGvhCM6wYXuDxNP5PUusHg==",
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": "^8.0.1",
|
||||||
|
"magic-string": "^0.25.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yufka/node_modules/acorn": {
|
||||||
|
"version": "8.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
|
||||||
|
"integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -10703,6 +10770,11 @@
|
|||||||
"integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==",
|
"integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"@types/pako": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-8UJl2MjkqqS6ncpLZqRZ5LmGiFBkbYxocD4e4jmBqGvfRG1RS23gKsBQbdtV9O9GvRyjFTiRHRByjSlKCLlmZw=="
|
||||||
|
},
|
||||||
"@types/plist": {
|
"@types/plist": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz",
|
||||||
@ -12588,6 +12660,14 @@
|
|||||||
"is-arrayish": "^0.2.1"
|
"is-arrayish": "^0.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"error-stack-parser": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==",
|
||||||
|
"requires": {
|
||||||
|
"stackframe": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"es-abstract": {
|
"es-abstract": {
|
||||||
"version": "1.18.5",
|
"version": "1.18.5",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz",
|
||||||
@ -12759,6 +12839,23 @@
|
|||||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"fast-png": {
|
||||||
|
"version": "5.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-5.0.4.tgz",
|
||||||
|
"integrity": "sha512-vTNj6yixRnclW6sTlCeH6sNRLBOhM5ITmlo1LSU5ojKEc2e9kZkqXPo2xzBxKb61MBCXRXBcr8qJztOHr2O6WQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/pako": "^1.0.1",
|
||||||
|
"iobuffer": "^5.0.2",
|
||||||
|
"pako": "^2.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pako": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"fastq": {
|
"fastq": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz",
|
||||||
@ -13618,6 +13715,11 @@
|
|||||||
"integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==",
|
"integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"iobuffer": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-0SNk4hbHVXx9oE27vTJY+oiI0txkhBdQV12RvILd/7XuIhBZ0TkImq5EnhFYCcRcDff8jpFhZ9C2Sg+NIo3ZMQ=="
|
||||||
|
},
|
||||||
"ip": {
|
"ip": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
||||||
@ -14719,6 +14821,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"magic-string": {
|
||||||
|
"version": "0.25.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||||
|
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
|
||||||
|
"requires": {
|
||||||
|
"sourcemap-codec": "^1.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
@ -16171,12 +16281,6 @@
|
|||||||
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
|
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pngjs": {
|
|
||||||
"version": "3.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
|
||||||
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"prelude-ls": {
|
"prelude-ls": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||||
@ -16879,6 +16983,11 @@
|
|||||||
"source-map": "^0.6.0"
|
"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": {
|
"spawn-wrap": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz",
|
||||||
@ -16973,6 +17082,11 @@
|
|||||||
"tweetnacl": "~0.14.0"
|
"tweetnacl": "~0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"stackframe": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA=="
|
||||||
|
},
|
||||||
"stat-mode": {
|
"stat-mode": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
|
||||||
@ -18055,6 +18169,22 @@
|
|||||||
"buffer-crc32": "~0.2.3",
|
"buffer-crc32": "~0.2.3",
|
||||||
"fd-slicer": "~1.1.0"
|
"fd-slicer": "~1.1.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"yufka": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yufka/-/yufka-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-VdqiJocmYfhx/yPiLFmF6ZyxKE2bzaHbocO0Y27sC5ytSQ09CLhTWP8GJAZMhGq2UGvhCM6wYXuDxNP5PUusHg==",
|
||||||
|
"requires": {
|
||||||
|
"acorn": "^8.0.1",
|
||||||
|
"magic-string": "^0.25.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "8.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
|
||||||
|
"integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,16 @@
|
|||||||
"@wasmer/wasmfs": "^0.12.0",
|
"@wasmer/wasmfs": "^0.12.0",
|
||||||
"binaryen": "^101.0.0",
|
"binaryen": "^101.0.0",
|
||||||
"clipboard": "^2.0.6",
|
"clipboard": "^2.0.6",
|
||||||
|
"error-stack-parser": "^2.0.6",
|
||||||
|
"fast-png": "^5.0.4",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"jszip": "^3.7.0",
|
"jszip": "^3.7.0",
|
||||||
"localforage": "^1.9.0",
|
"localforage": "^1.9.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"octokat": "^0.10.0",
|
"octokat": "^0.10.0",
|
||||||
"split.js": "^1.6.2"
|
"split.js": "^1.6.2",
|
||||||
|
"yufka": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bootbox": "^5.1.3",
|
"@types/bootbox": "^5.1.3",
|
||||||
@ -42,7 +45,6 @@
|
|||||||
"lzg": "^1.0.x",
|
"lzg": "^1.0.x",
|
||||||
"mocha": "^7.2.0",
|
"mocha": "^7.2.0",
|
||||||
"mocha-simple-html-reporter": "^2.0.0",
|
"mocha-simple-html-reporter": "^2.0.0",
|
||||||
"pngjs": "^3.4.0",
|
|
||||||
"typedoc": "^0.21.0",
|
"typedoc": "^0.21.0",
|
||||||
"typescript": "^4.3.4",
|
"typescript": "^4.3.4",
|
||||||
"typescript-formatter": "^7.2.2"
|
"typescript-formatter": "^7.2.2"
|
||||||
|
@ -31,8 +31,8 @@ export function setNoiseSeed(x : number) {
|
|||||||
|
|
||||||
type KeyboardCallback = (which:number, charCode:number, flags:KeyFlags) => void;
|
type KeyboardCallback = (which:number, charCode:number, flags:KeyFlags) => void;
|
||||||
|
|
||||||
function __createCanvas(mainElement:HTMLElement, width:number, height:number) : HTMLCanvasElement {
|
function __createCanvas(doc:HTMLDocument, mainElement:HTMLElement, width:number, height:number) : HTMLCanvasElement {
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = doc.createElement('canvas');
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
canvas.classList.add("emuvideo");
|
canvas.classList.add("emuvideo");
|
||||||
@ -104,9 +104,9 @@ export class RasterVideo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create(doc?: HTMLDocument) {
|
||||||
var canvas;
|
var canvas;
|
||||||
this.canvas = canvas = __createCanvas(this.mainElement, this.width, this.height);
|
this.canvas = canvas = __createCanvas(doc || document, this.mainElement, this.width, this.height);
|
||||||
this.vcanvas = $(canvas);
|
this.vcanvas = $(canvas);
|
||||||
if (this.options && this.options.rotate) {
|
if (this.options && this.options.rotate) {
|
||||||
this.setRotate(this.options.rotate);
|
this.setRotate(this.options.rotate);
|
||||||
|
159
src/common/script/bitmap.ts
Normal file
159
src/common/script/bitmap.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
import * as fastpng from 'fast-png';
|
||||||
|
import { convertWordsToImages, PixelEditorImageFormat } from '../../ide/pixeleditor';
|
||||||
|
import { arrayCompare } from '../util';
|
||||||
|
import * as io from './io'
|
||||||
|
|
||||||
|
export abstract class AbstractBitmap {
|
||||||
|
constructor(
|
||||||
|
public readonly width: number,
|
||||||
|
public readonly height: number,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RGBABitmap extends AbstractBitmap {
|
||||||
|
public readonly rgba: Uint32Array
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
) {
|
||||||
|
super(width, height);
|
||||||
|
this.rgba = new Uint32Array(this.width * this.height);
|
||||||
|
}
|
||||||
|
setPixel(x: number, y: number, rgba: number): void {
|
||||||
|
this.rgba[y * this.width + x] = rgba;
|
||||||
|
}
|
||||||
|
getPixel(x: number, y: number): number {
|
||||||
|
return this.rgba[y * this.width + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class MappedBitmap extends AbstractBitmap {
|
||||||
|
public readonly pixels: Uint8Array
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
public readonly bitsPerPixel: number,
|
||||||
|
pixels?: Uint8Array
|
||||||
|
) {
|
||||||
|
super(width, height);
|
||||||
|
this.pixels = pixels || new Uint8Array(this.width * this.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPixel(x: number, y: number, index: number): void {
|
||||||
|
this.pixels[y * this.width + x] = index;
|
||||||
|
}
|
||||||
|
getPixel(x: number, y: number): number {
|
||||||
|
return this.pixels[y * this.width + x];
|
||||||
|
}
|
||||||
|
abstract getRGBAForIndex(index: number): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Palette {
|
||||||
|
colors: Uint32Array;
|
||||||
|
|
||||||
|
constructor(numColors: number) {
|
||||||
|
this.colors = new Uint32Array(numColors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IndexedBitmap extends MappedBitmap {
|
||||||
|
public palette: Palette;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
bitsPerPixel: number,
|
||||||
|
pixels?: Uint8Array
|
||||||
|
) {
|
||||||
|
super(width, height, bitsPerPixel, pixels);
|
||||||
|
this.palette = getDefaultPalette(bitsPerPixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRGBAForIndex(index: number): number {
|
||||||
|
return this.palette.colors[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO?
|
||||||
|
function getDefaultPalette(bpp: number) {
|
||||||
|
var pal = new Palette(1 << bpp);
|
||||||
|
for (var i=0; i<pal.colors.length; i++) {
|
||||||
|
pal.colors[i] = 0xff000000 | (i * 7919);
|
||||||
|
}
|
||||||
|
return pal;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rgbaToUint32(rgba: number[]): number {
|
||||||
|
let v = 0;
|
||||||
|
v |= rgba[0] << 0;
|
||||||
|
v |= rgba[1] << 8;
|
||||||
|
v |= rgba[2] << 16;
|
||||||
|
v |= rgba[3] << 24;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rgba(width: number, height: number) {
|
||||||
|
return new RGBABitmap(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function indexed(width: number, height: number, bpp: number) {
|
||||||
|
return new IndexedBitmap(width, height, bpp);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BitmapType = RGBABitmap | IndexedBitmap;
|
||||||
|
|
||||||
|
export namespace png {
|
||||||
|
export function load(url: string): BitmapType {
|
||||||
|
return decode(io.loadbin(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 = <any>png.palette as [number, number, number, number][];
|
||||||
|
var palette = new Palette(palarr.length);
|
||||||
|
for (let i = 0; i < palarr.length; i++) {
|
||||||
|
// TODO: alpha?
|
||||||
|
palette.colors[i] = rgbaToUint32(palarr[i]) | 0xff000000;
|
||||||
|
}
|
||||||
|
let bitmap = new IndexedBitmap(png.width, png.height, png.depth);
|
||||||
|
for (let i = 0; i < bitmap.pixels.length; i++) {
|
||||||
|
bitmap.pixels[i] = png.data[i];
|
||||||
|
}
|
||||||
|
bitmap.palette = palette;
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
function convertRGBAToBitmap(png: fastpng.IDecodedPNG): RGBABitmap {
|
||||||
|
let bitmap = new RGBABitmap(png.width, png.height);
|
||||||
|
let rgba = [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] = rgbaToUint32(rgba);
|
||||||
|
}
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace from {
|
||||||
|
// TODO: check arguments
|
||||||
|
export function bytes(arr: Uint8Array, fmt: PixelEditorImageFormat) {
|
||||||
|
var pixels = convertWordsToImages(arr, fmt);
|
||||||
|
// TODO: guess if missing w/h/count?
|
||||||
|
// TODO: reverse mapping
|
||||||
|
// TODO: maybe better composable functions
|
||||||
|
return pixels.map(data => new IndexedBitmap(fmt.w, fmt.h, fmt.bpp|1, data));
|
||||||
|
}
|
||||||
|
}
|
125
src/common/script/env.ts
Normal file
125
src/common/script/env.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { WorkerError } from "../workertypes";
|
||||||
|
import ErrorStackParser = require("error-stack-parser");
|
||||||
|
import yufka from 'yufka';
|
||||||
|
import * as bitmap from "./bitmap";
|
||||||
|
import * as io from "./io";
|
||||||
|
import * as output from "./output";
|
||||||
|
import { escapeHTML } from "../util";
|
||||||
|
|
||||||
|
export interface Cell {
|
||||||
|
id: string;
|
||||||
|
object?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IMPORTS = {
|
||||||
|
'bitmap': bitmap,
|
||||||
|
'io': io,
|
||||||
|
'output': output
|
||||||
|
}
|
||||||
|
|
||||||
|
const LINE_NUMBER_OFFSET = 3;
|
||||||
|
|
||||||
|
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',
|
||||||
|
]
|
||||||
|
|
||||||
|
export class Environment {
|
||||||
|
preamble: string;
|
||||||
|
postamble: string;
|
||||||
|
obj: {};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly globalenv: any,
|
||||||
|
public readonly path: string
|
||||||
|
) {
|
||||||
|
var badlst = Object.getOwnPropertyNames(this.globalenv).filter(name => GLOBAL_GOODLIST.indexOf(name) < 0);
|
||||||
|
this.preamble = `'use strict';var ${badlst.join(',')};`;
|
||||||
|
for (var impname in IMPORTS) {
|
||||||
|
this.preamble += `var ${impname}=$$.${impname};`
|
||||||
|
}
|
||||||
|
this.preamble += '{\n';
|
||||||
|
this.postamble = '\n}';
|
||||||
|
}
|
||||||
|
preprocess(code: string): string {
|
||||||
|
var declvars = {};
|
||||||
|
const result = yufka(code, (node, { update, source, parent }) => {
|
||||||
|
switch (node.type) {
|
||||||
|
case 'Identifier':
|
||||||
|
if (GLOBAL_BADLIST.indexOf(source()) >= 0) {
|
||||||
|
update(`__FORBIDDEN__KEYWORD__${source()}__`) // TODO? how to preserve line number?
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'AssignmentExpression':
|
||||||
|
/*
|
||||||
|
// x = expr --> var x = expr
|
||||||
|
if (parent().type === 'ExpressionStatement' && parent(2) && parent(2).type === 'Program') { // TODO
|
||||||
|
let left = node['left'];
|
||||||
|
if (left && left.type === 'Identifier' && declvars[left.name] == null) {
|
||||||
|
update(`var ${source()}`)
|
||||||
|
declvars[left.name] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
async run(code: string): Promise<void> {
|
||||||
|
code = this.preprocess(code);
|
||||||
|
this.obj = {};
|
||||||
|
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
|
||||||
|
const fn = new AsyncFunction('$$', this.preamble + code + this.postamble).bind(this.obj, IMPORTS);
|
||||||
|
await fn.call(this);
|
||||||
|
this.checkResult();
|
||||||
|
}
|
||||||
|
checkResult() {
|
||||||
|
for (var [key, value] of Object.entries(this.obj)) {
|
||||||
|
if (value instanceof Promise) {
|
||||||
|
throw new Error(`'${key}' is unresolved. Use 'await' before expression.`) // TODO?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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[] {
|
||||||
|
if (e['loc'] != null) {
|
||||||
|
return [{
|
||||||
|
path: this.path,
|
||||||
|
msg: e.message,
|
||||||
|
line: e['loc'].line,
|
||||||
|
start: e['loc'].column,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
// TODO: Cannot parse given Error object
|
||||||
|
var frames = ErrorStackParser.parse(e);
|
||||||
|
var frame = frames.find(f => f.functionName === 'anonymous');
|
||||||
|
return [{
|
||||||
|
path: this.path,
|
||||||
|
msg: e.message,
|
||||||
|
line: frame ? frame.lineNumber - LINE_NUMBER_OFFSET : 0,
|
||||||
|
start: frame ? frame.columnNumber : 0,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
69
src/common/script/io.ts
Normal file
69
src/common/script/io.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { ProjectFilesystem } from "../../ide/project";
|
||||||
|
import { FileData } from "../workertypes";
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
var $$fs: ProjectFilesystem;
|
||||||
|
var $$cache: { [path: string]: FileData } = {};
|
||||||
|
|
||||||
|
export class IOWaitError extends Error {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function $$setupFS(fs: ProjectFilesystem) {
|
||||||
|
$$fs = fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFS(): ProjectFilesystem {
|
||||||
|
if ($$fs == null) throw new Error(`Internal Error: The 'io' module has not been set up properly.`)
|
||||||
|
return $$fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 load(url: string, type?: 'binary' | 'text'): FileData {
|
||||||
|
url = canonicalurl(url);
|
||||||
|
// 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 new Uint8Array(xhr.response);
|
||||||
|
} else {
|
||||||
|
return xhr.response;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`The resource at "${url}" responded with status code of ${xhr.status}.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadbin(url: string): Uint8Array {
|
||||||
|
var data = load(url, 'binary');
|
||||||
|
if (data instanceof Uint8Array)
|
||||||
|
return data;
|
||||||
|
else
|
||||||
|
throw new Error(`The resource at "${url}" is not a binary file.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function xload(path: string): FileData {
|
||||||
|
var data = $$cache[path];
|
||||||
|
if (data == null) {
|
||||||
|
getFS().getFileData(path).then((value) => {
|
||||||
|
$$cache[path] = value;
|
||||||
|
})
|
||||||
|
throw new IOWaitError(path);
|
||||||
|
} else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
165
src/common/script/node.ts
Normal file
165
src/common/script/node.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
|
||||||
|
var lastTimestamp = 0;
|
||||||
|
|
||||||
|
function newTimestamp() {
|
||||||
|
return ++lastTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DependencySet = {[id:string] : ComputeNode | any}
|
||||||
|
|
||||||
|
export abstract class ComputeNode {
|
||||||
|
private src_ts: number = newTimestamp();
|
||||||
|
private result_ts: number = 0;
|
||||||
|
private depends: DependencySet = {};
|
||||||
|
private busy: Promise<void> = null;
|
||||||
|
|
||||||
|
modified() {
|
||||||
|
this.src_ts = newTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
isStale(ts: number) {
|
||||||
|
return this.result_ts < ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDependencies(depends: DependencySet) {
|
||||||
|
this.depends = depends;
|
||||||
|
// compute latest timestamp of all dependencies
|
||||||
|
var ts = 0;
|
||||||
|
for (let [key, dep] of Object.entries(this.depends)) {
|
||||||
|
if (dep instanceof ComputeNode && dep.result_ts) {
|
||||||
|
ts = Math.max(ts, dep.result_ts);
|
||||||
|
} else {
|
||||||
|
ts = newTimestamp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.src_ts = ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencies() {
|
||||||
|
return this.depends;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() : Promise<void> {
|
||||||
|
let maxts = 0;
|
||||||
|
let dependsComputes = []
|
||||||
|
for (let [key, dep] of Object.entries(this.depends)) {
|
||||||
|
if (dep instanceof ComputeNode && dep.isStale(this.src_ts)) {
|
||||||
|
dependsComputes.push(dep.compute());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dependsComputes.length) {
|
||||||
|
await Promise.all(dependsComputes);
|
||||||
|
this.recompute(maxts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async recompute(ts: number) : Promise<void> {
|
||||||
|
// are we currently waiting for a computation to finish?
|
||||||
|
if (this.busy == null || ts > this.result_ts) {
|
||||||
|
// wait for previous operation to finish (no-op if null)
|
||||||
|
await this.busy;
|
||||||
|
this.result_ts = ts;
|
||||||
|
this.busy = this.compute();
|
||||||
|
}
|
||||||
|
await this.busy;
|
||||||
|
this.busy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract compute(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValueNode<T> extends ComputeNode {
|
||||||
|
private value : T;
|
||||||
|
|
||||||
|
constructor(value : T) {
|
||||||
|
super();
|
||||||
|
this.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get() : T {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(newValue : T) {
|
||||||
|
this.value = newValue;
|
||||||
|
this.modified();
|
||||||
|
}
|
||||||
|
|
||||||
|
async compute() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArrayNode<T> extends ValueNode<T> {
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntegerNode extends ValueNode<number> {
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BitmapNode extends ComputeNode {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RGBABitmapNode extends BitmapNode {
|
||||||
|
rgba: ArrayNode<Uint32Array>;
|
||||||
|
|
||||||
|
compute(): Promise<void> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IndexedBitmapNode extends BitmapNode {
|
||||||
|
indices: ArrayNode<Uint8Array>;
|
||||||
|
|
||||||
|
compute(): Promise<void> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaletteNode {
|
||||||
|
colors: ArrayNode<Uint32Array>;
|
||||||
|
|
||||||
|
compute(): Promise<void> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaletteMapNode extends ComputeNode {
|
||||||
|
palette: PaletteNode;
|
||||||
|
indices: ArrayNode<Uint8Array>;
|
||||||
|
|
||||||
|
compute(): Promise<void> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function valueOf<T>(node : ValueNode<T>) : T {
|
||||||
|
return node.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestNode extends ComputeNode {
|
||||||
|
value : string;
|
||||||
|
|
||||||
|
async compute() {
|
||||||
|
await new Promise(r => setTimeout(r, 100));
|
||||||
|
this.value = Object.values(this.getDependencies()).map(valueOf).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
async function test() {
|
||||||
|
var val1 = new ValueNode<number>(1234);
|
||||||
|
var arr1 = new ValueNode<number[]>([1,2,3]);
|
||||||
|
var join = new TestNode();
|
||||||
|
join.setDependencies({a:val1, b:arr1});
|
||||||
|
await join.update();
|
||||||
|
console.log(join);
|
||||||
|
val1.set(9999);
|
||||||
|
join.update();
|
||||||
|
val1.set(9989)
|
||||||
|
await join.update();
|
||||||
|
console.log(join);
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
85
src/common/script/output.ts
Normal file
85
src/common/script/output.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
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 <stdint.h>\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);
|
||||||
|
}
|
13
src/common/script/test.ts
Normal file
13
src/common/script/test.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
import 'fs';
|
||||||
|
import * as bitmap from './bitmap'
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
var data = fs.readFileSync('images/book_a2600.png');
|
||||||
|
//var data = fs.readFileSync('images/print-head.png');
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
var png = bitmap.png.decode(data);
|
||||||
|
console.log(png)
|
||||||
|
|
@ -1,3 +1,4 @@
|
|||||||
|
import { UintArray } from "../ide/pixeleditor";
|
||||||
|
|
||||||
export function lpad(s:string, n:number):string {
|
export function lpad(s:string, n:number):string {
|
||||||
s += ''; // convert to string
|
s += ''; // convert to string
|
||||||
@ -55,7 +56,7 @@ export function toradix(v:number, nd:number, radix:number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function arrayCompare(a:any[], b:any[]):boolean {
|
export function arrayCompare(a:any[]|UintArray, b:any[]|UintArray):boolean {
|
||||||
if (a == null && b == null) return true;
|
if (a == null && b == null) return true;
|
||||||
if (a == null) return false;
|
if (a == null) return false;
|
||||||
if (b == null) return false;
|
if (b == null) return false;
|
||||||
@ -618,3 +619,7 @@ export function parseXMLPoorly(s: string, openfn?: XMLVisitFunction, closefn?: X
|
|||||||
return top;
|
return top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function escapeHTML(s: string): string {
|
||||||
|
return s.replace(/[&]/g, '&').replace(/[<]/g, '<').replace(/[>]/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ function reindexMask(x:number, inds:number[]) : [number, number] {
|
|||||||
return [i >> 3, i & 7];
|
return [i >> 3, i & 7];
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertWordsToImages(words:UintArray, fmt:PixelEditorImageFormat) : Uint8Array[] {
|
export function convertWordsToImages(words:UintArray, fmt:PixelEditorImageFormat) : Uint8Array[] {
|
||||||
var width = fmt.w;
|
var width = fmt.w;
|
||||||
var height = fmt.h;
|
var height = fmt.h;
|
||||||
var count = fmt.count || 1;
|
var count = fmt.count || 1;
|
||||||
@ -190,7 +190,7 @@ function convertWordsToImages(words:UintArray, fmt:PixelEditorImageFormat) : Uin
|
|||||||
return images;
|
return images;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertImagesToWords(images:Uint8Array[], fmt:PixelEditorImageFormat) : number[] {
|
export function convertImagesToWords(images:Uint8Array[], fmt:PixelEditorImageFormat) : number[] {
|
||||||
if (fmt.destfmt) fmt = fmt.destfmt;
|
if (fmt.destfmt) fmt = fmt.destfmt;
|
||||||
var width = fmt.w;
|
var width = fmt.w;
|
||||||
var height = fmt.h;
|
var height = fmt.h;
|
||||||
@ -236,7 +236,7 @@ function convertImagesToWords(images:Uint8Array[], fmt:PixelEditorImageFormat) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
function convertPaletteBytes(arr:UintArray,r0,r1,g0,g1,b0,b1) : number[] {
|
export function convertPaletteBytes(arr:UintArray,r0,r1,g0,g1,b0,b1) : number[] {
|
||||||
var result = [];
|
var result = [];
|
||||||
for (var i=0; i<arr.length; i++) {
|
for (var i=0; i<arr.length; i++) {
|
||||||
var d = arr[i];
|
var d = arr[i];
|
||||||
@ -266,7 +266,7 @@ export function getPaletteLength(palfmt: PixelEditorPaletteFormat) : number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertPaletteFormat(palbytes:UintArray, palfmt: PixelEditorPaletteFormat) : number[] {
|
export function convertPaletteFormat(palbytes:UintArray, palfmt: PixelEditorPaletteFormat) : number[] {
|
||||||
var pal = palfmt.pal;
|
var pal = palfmt.pal;
|
||||||
var newpalette;
|
var newpalette;
|
||||||
if (typeof pal === 'number') {
|
if (typeof pal === 'number') {
|
||||||
@ -290,7 +290,7 @@ function convertPaletteFormat(palbytes:UintArray, palfmt: PixelEditorPaletteForm
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: illegal colors?
|
// TODO: illegal colors?
|
||||||
var PREDEF_PALETTES = {
|
const PREDEF_PALETTES = {
|
||||||
'nes':[
|
'nes':[
|
||||||
0x525252, 0xB40000, 0xA00000, 0xB1003D, 0x740069, 0x00005B, 0x00005F, 0x001840, 0x002F10, 0x084A08, 0x006700, 0x124200, 0x6D2800, 0x000000, 0x000000, 0x000000,
|
0x525252, 0xB40000, 0xA00000, 0xB1003D, 0x740069, 0x00005B, 0x00005F, 0x001840, 0x002F10, 0x084A08, 0x006700, 0x124200, 0x6D2800, 0x000000, 0x000000, 0x000000,
|
||||||
0xC4D5E7, 0xFF4000, 0xDC0E22, 0xFF476B, 0xD7009F, 0x680AD7, 0x0019BC, 0x0054B1, 0x006A5B, 0x008C03, 0x00AB00, 0x2C8800, 0xA47200, 0x000000, 0x000000, 0x000000,
|
0xC4D5E7, 0xFF4000, 0xDC0E22, 0xFF476B, 0xD7009F, 0x680AD7, 0x0019BC, 0x0054B1, 0x006A5B, 0x008C03, 0x00AB00, 0x2C8800, 0xA47200, 0x000000, 0x000000, 0x000000,
|
||||||
|
@ -221,16 +221,20 @@ function getCurrentPresetTitle() : string {
|
|||||||
return current_preset.title || current_preset.name || current_project.mainPath || "ROM";
|
return current_preset.title || current_preset.name || current_project.mainPath || "ROM";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initProject() {
|
async function newFilesystem() {
|
||||||
var basefs : ProjectFilesystem = new WebPresetsFileSystem(platform_id);
|
var basefs : ProjectFilesystem = new WebPresetsFileSystem(platform_id);
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
console.log('using electron with local filesystem', alternateLocalFilesystem);
|
console.log('using electron with local filesystem', alternateLocalFilesystem);
|
||||||
var filesystem = new OverlayFilesystem(basefs, alternateLocalFilesystem);
|
return new OverlayFilesystem(basefs, alternateLocalFilesystem);
|
||||||
} else if (qs.localfs != null) {
|
} else if (qs.localfs != null) {
|
||||||
var filesystem = new OverlayFilesystem(basefs, await getLocalFilesystem(qs.localfs));
|
return new OverlayFilesystem(basefs, await getLocalFilesystem(qs.localfs));
|
||||||
} else {
|
} else {
|
||||||
var filesystem = new OverlayFilesystem(basefs, new LocalForageFilesystem(store));
|
return new OverlayFilesystem(basefs, new LocalForageFilesystem(store));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initProject() {
|
||||||
|
var filesystem = await newFilesystem();
|
||||||
current_project = new CodeProject(newWorker(), platform_id, platform, filesystem);
|
current_project = new CodeProject(newWorker(), platform_id, platform, filesystem);
|
||||||
projectWindows = new ProjectWindows($("#workspace")[0] as HTMLElement, current_project);
|
projectWindows = new ProjectWindows($("#workspace")[0] as HTMLElement, current_project);
|
||||||
current_project.callbackBuildResult = (result:WorkerResult) => {
|
current_project.callbackBuildResult = (result:WorkerResult) => {
|
||||||
|
@ -17,6 +17,7 @@ export function importPlatform(name: string) : Promise<any> {
|
|||||||
case "msx": return import("../platform/msx");
|
case "msx": return import("../platform/msx");
|
||||||
case "mw8080bw": return import("../platform/mw8080bw");
|
case "mw8080bw": return import("../platform/mw8080bw");
|
||||||
case "nes": return import("../platform/nes");
|
case "nes": return import("../platform/nes");
|
||||||
|
case "script": return import("../platform/script");
|
||||||
case "sms": return import("../platform/sms");
|
case "sms": return import("../platform/sms");
|
||||||
case "sound_konami": return import("../platform/sound_konami");
|
case "sound_konami": return import("../platform/sound_konami");
|
||||||
case "sound_williams": return import("../platform/sound_williams");
|
case "sound_williams": return import("../platform/sound_williams");
|
||||||
|
164
src/platform/script.ts
Normal file
164
src/platform/script.ts
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
|
||||||
|
import { PLATFORMS, RasterVideo } from "../common/emu";
|
||||||
|
import { Platform } from "../common/baseplatform";
|
||||||
|
import { Cell } from "../common/script/env";
|
||||||
|
import { escapeHTML } from "../common/util";
|
||||||
|
import { BitmapType, IndexedBitmap, RGBABitmap } from "../common/script/bitmap";
|
||||||
|
|
||||||
|
abstract class TileEditor<T> {
|
||||||
|
video: RasterVideo;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly tileWidth: number,
|
||||||
|
public readonly tileHeight: number,
|
||||||
|
public readonly numColumns: number,
|
||||||
|
public readonly numRows: number,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
getPixelWidth() { return this.tileWidth * this.numColumns }
|
||||||
|
getPixelHeight() { return this.tileHeight * this.numRows }
|
||||||
|
attach(div: HTMLElement) {
|
||||||
|
this.video = new RasterVideo(div, this.getPixelWidth(), this.getPixelHeight());
|
||||||
|
this.video.create();
|
||||||
|
}
|
||||||
|
detach() {
|
||||||
|
this.video = null;
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
if (this.video) this.video.updateFrame();
|
||||||
|
}
|
||||||
|
abstract getTile(x: number, y: number): T;
|
||||||
|
abstract renderTile(x: number, y: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RGBABitmapEditor extends TileEditor<number> {
|
||||||
|
constructor(
|
||||||
|
public readonly bitmap: RGBABitmap
|
||||||
|
) {
|
||||||
|
super(1, 1, bitmap.width, bitmap.height);
|
||||||
|
}
|
||||||
|
getTile(x: number, y: number) {
|
||||||
|
return this.bitmap.getPixel(x, y);
|
||||||
|
}
|
||||||
|
renderTile(x: number, y: number): void {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bitmap2image(doc: HTMLDocument, div: HTMLElement, bitmap: BitmapType): HTMLCanvasElement {
|
||||||
|
var video = new RasterVideo(div, bitmap.width, bitmap.height);
|
||||||
|
video.create(doc);
|
||||||
|
video.canvas.className = 'pixelated';
|
||||||
|
let vdata = video.getFrameData();
|
||||||
|
if (bitmap['palette'] != null) {
|
||||||
|
let bmp = bitmap as IndexedBitmap;
|
||||||
|
let pal = bmp.palette.colors;
|
||||||
|
for (var i = 0; i < bmp.pixels.length; i++) {
|
||||||
|
vdata[i] = pal[bmp.pixels[i]];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let bmp = bitmap as RGBABitmap;
|
||||||
|
vdata.set(bmp.rgba);
|
||||||
|
}
|
||||||
|
video.updateFrame();
|
||||||
|
return video.canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Notebook {
|
||||||
|
constructor(
|
||||||
|
public readonly maindoc: HTMLDocument,
|
||||||
|
public readonly maindiv: HTMLElement
|
||||||
|
) {
|
||||||
|
maindiv.classList.add('vertical-scroll');
|
||||||
|
//maindiv.classList.add('container')
|
||||||
|
}
|
||||||
|
updateCells(cells: Cell[]) {
|
||||||
|
let body = this.maindiv;
|
||||||
|
body.innerHTML = '';
|
||||||
|
//var body = $(this.iframe).contents().find('body');
|
||||||
|
//body.empty();
|
||||||
|
for (let cell of cells) {
|
||||||
|
if (cell.object != null) {
|
||||||
|
let div = this.objectToDiv(cell.object);
|
||||||
|
div.id = cell.id;
|
||||||
|
div.classList.add('scripting-cell')
|
||||||
|
//div.classList.add('row')
|
||||||
|
body.append(div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objectToDiv(object: any) {
|
||||||
|
let div = document.createElement('div');
|
||||||
|
//div.classList.add('col-auto')
|
||||||
|
//grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
|
||||||
|
// TODO: tile editor
|
||||||
|
if (Array.isArray(object) || object.BYTES_PER_ELEMENT) {
|
||||||
|
object.forEach((obj) => {
|
||||||
|
div.appendChild(this.objectToDiv(obj));
|
||||||
|
});
|
||||||
|
// TODO
|
||||||
|
} else if (object['bitsPerPixel'] && object['pixels'] && object['palette']) {
|
||||||
|
bitmap2image(this.maindoc, div, object as IndexedBitmap);
|
||||||
|
} else if (object['rgba']) {
|
||||||
|
bitmap2image(this.maindoc, div, object as RGBABitmap);
|
||||||
|
} else if (object != null) {
|
||||||
|
div.innerHTML = escapeHTML(JSON.stringify(object));
|
||||||
|
}
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptingPlatform implements Platform {
|
||||||
|
mainElement: HTMLElement;
|
||||||
|
iframe: HTMLIFrameElement;
|
||||||
|
notebook: Notebook;
|
||||||
|
|
||||||
|
constructor(mainElement: HTMLElement) {
|
||||||
|
this.mainElement = mainElement;
|
||||||
|
/*
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
|
||||||
|
this.iframe = $(`<iframe sandbox="allow-same-origin" width="100%" height="100%"/>`).appendTo(mainElement)[0] as HTMLIFrameElement;
|
||||||
|
mainElement.classList.add("vertical-scroll"); //100% height
|
||||||
|
mainElement.style.overflowY = 'auto';
|
||||||
|
this.iframe.onload = (e) => {
|
||||||
|
let head = this.iframe.contentDocument.head;
|
||||||
|
head.appendChild($(`<link rel="stylesheet" href="css/script.css">`)[0]);
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
this.notebook = new Notebook(document, mainElement);
|
||||||
|
}
|
||||||
|
start() {
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
}
|
||||||
|
pause() {
|
||||||
|
}
|
||||||
|
resume() {
|
||||||
|
}
|
||||||
|
loadROM(title, cells: Cell[]) {
|
||||||
|
this.notebook.updateCells(cells);
|
||||||
|
}
|
||||||
|
isRunning() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
isDebugging(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
getToolForFilename(fn: string): string {
|
||||||
|
return "js";
|
||||||
|
}
|
||||||
|
getDefaultExtension(): string {
|
||||||
|
return ".js";
|
||||||
|
}
|
||||||
|
getPresets() {
|
||||||
|
return [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
showHelp() {
|
||||||
|
window.open("https://github.com/showdownjs/showdown/wiki/Showdown's-Markdown-syntax", "_help");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
PLATFORMS['script'] = ScriptingPlatform;
|
29
src/worker/tools/script.ts
Normal file
29
src/worker/tools/script.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
import { Environment } from "../../common/script/env";
|
||||||
|
import { BuildStep, BuildStepResult, emglobal, store } from "../workermain";
|
||||||
|
|
||||||
|
// 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<BuildStepResult> {
|
||||||
|
var env = getEnv(step.path);
|
||||||
|
var code = store.getFileAsString(step.path);
|
||||||
|
try {
|
||||||
|
await env.run(code);
|
||||||
|
} catch (e) {
|
||||||
|
return {errors: env.extractErrors(e)};
|
||||||
|
}
|
||||||
|
var output = env.render();
|
||||||
|
return {
|
||||||
|
output
|
||||||
|
};
|
||||||
|
}
|
@ -394,7 +394,7 @@ export interface BuildStep extends WorkerBuildStep {
|
|||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
class FileWorkingStore {
|
export class FileWorkingStore {
|
||||||
workfs : {[path:string]:FileEntry} = {};
|
workfs : {[path:string]:FileEntry} = {};
|
||||||
workerseq : number = 0;
|
workerseq : number = 0;
|
||||||
|
|
||||||
@ -452,7 +452,7 @@ class Builder {
|
|||||||
wasChanged(entry:FileEntry) : boolean {
|
wasChanged(entry:FileEntry) : boolean {
|
||||||
return entry.ts > this.startseq;
|
return entry.ts > this.startseq;
|
||||||
}
|
}
|
||||||
executeBuildSteps() {
|
async executeBuildSteps() : Promise<WorkerResult> {
|
||||||
this.startseq = store.currentVersion();
|
this.startseq = store.currentVersion();
|
||||||
var linkstep : BuildStep = null;
|
var linkstep : BuildStep = null;
|
||||||
while (this.steps.length) {
|
while (this.steps.length) {
|
||||||
@ -462,7 +462,7 @@ class Builder {
|
|||||||
if (!toolfn) throw Error("no tool named " + step.tool);
|
if (!toolfn) throw Error("no tool named " + step.tool);
|
||||||
step.params = PLATFORM_PARAMS[getBasePlatform(platform)];
|
step.params = PLATFORM_PARAMS[getBasePlatform(platform)];
|
||||||
try {
|
try {
|
||||||
step.result = toolfn(step);
|
step.result = await toolfn(step);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("EXCEPTION", e, e.stack);
|
console.log("EXCEPTION", e, e.stack);
|
||||||
return {errors:[{line:0, msg:e+""}]}; // TODO: catch errors already generated?
|
return {errors:[{line:0, msg:e+""}]}; // TODO: catch errors already generated?
|
||||||
@ -509,7 +509,7 @@ class Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleMessage(data: WorkerMessage) : WorkerResult {
|
async handleMessage(data: WorkerMessage) : Promise<WorkerResult> {
|
||||||
this.steps = [];
|
this.steps = [];
|
||||||
// file updates
|
// file updates
|
||||||
if (data.updates) {
|
if (data.updates) {
|
||||||
@ -528,7 +528,7 @@ class Builder {
|
|||||||
}
|
}
|
||||||
// execute build steps
|
// execute build steps
|
||||||
if (this.steps.length) {
|
if (this.steps.length) {
|
||||||
var result = this.executeBuildSteps();
|
var result = await this.executeBuildSteps();
|
||||||
return result ? result : {unchanged:true};
|
return result ? result : {unchanged:true};
|
||||||
}
|
}
|
||||||
// TODO: cache results
|
// TODO: cache results
|
||||||
@ -1029,6 +1029,7 @@ import * as m6502 from './tools/m6502'
|
|||||||
import * as z80 from './tools/z80'
|
import * as z80 from './tools/z80'
|
||||||
import * as x86 from './tools/x86'
|
import * as x86 from './tools/x86'
|
||||||
import * as arm from './tools/arm'
|
import * as arm from './tools/arm'
|
||||||
|
import * as script from './tools/script'
|
||||||
|
|
||||||
var TOOLS = {
|
var TOOLS = {
|
||||||
'dasm': dasm.assembleDASM,
|
'dasm': dasm.assembleDASM,
|
||||||
@ -1064,6 +1065,7 @@ var TOOLS = {
|
|||||||
'wiz': misc.compileWiz,
|
'wiz': misc.compileWiz,
|
||||||
'armips': arm.assembleARMIPS,
|
'armips': arm.assembleARMIPS,
|
||||||
'vasmarm': arm.assembleVASMARM,
|
'vasmarm': arm.assembleVASMARM,
|
||||||
|
'js': script.runJavascript,
|
||||||
}
|
}
|
||||||
|
|
||||||
var TOOL_PRELOADFS = {
|
var TOOL_PRELOADFS = {
|
||||||
@ -1092,7 +1094,9 @@ var TOOL_PRELOADFS = {
|
|||||||
'wiz': 'wiz',
|
'wiz': 'wiz',
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMessage(data : WorkerMessage) : WorkerResult {
|
const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay));
|
||||||
|
|
||||||
|
async function handleMessage(data : WorkerMessage) : Promise<WorkerResult> {
|
||||||
// preload file system
|
// preload file system
|
||||||
if (data.preload) {
|
if (data.preload) {
|
||||||
var fs = TOOL_PRELOADFS[data.preload];
|
var fs = TOOL_PRELOADFS[data.preload];
|
||||||
@ -1113,12 +1117,15 @@ function handleMessage(data : WorkerMessage) : WorkerResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ENVIRONMENT_IS_WORKER) {
|
if (ENVIRONMENT_IS_WORKER) {
|
||||||
onmessage = function(e) {
|
var lastpromise = null;
|
||||||
var result = handleMessage(e.data);
|
onmessage = async function(e) {
|
||||||
|
await lastpromise; // wait for previous message to complete
|
||||||
|
lastpromise = handleMessage(e.data);
|
||||||
|
var result = await lastpromise;
|
||||||
|
lastpromise = null;
|
||||||
if (result) {
|
if (result) {
|
||||||
postMessage(result);
|
postMessage(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//}();
|
|
||||||
|
Loading…
Reference in New Issue
Block a user