mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-04-07 23:37:22 +00:00
scripting: fixed cache, io.module(), return values, button, blank()
This commit is contained in:
parent
6343c75953
commit
5542555193
16
css/ui.css
16
css/ui.css
@ -802,9 +802,8 @@ div.scripting-editor {
|
||||
max-height: 50vw;
|
||||
}
|
||||
div.scripting-color {
|
||||
padding: 0.5em;
|
||||
min-width: 3em;
|
||||
min-height: 3em;
|
||||
min-width: 2em;
|
||||
min-height: 2em;
|
||||
border: 3px solid black;
|
||||
}
|
||||
div.scripting-color span {
|
||||
@ -836,3 +835,14 @@ div.scripting-select > .scripting-selected {
|
||||
border-style: solid;
|
||||
border-color: #eee;
|
||||
}
|
||||
div.scripting-cell button {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
padding: 0.5em;
|
||||
}
|
||||
div.scripting-cell button:hover {
|
||||
border-color: #fff;
|
||||
}
|
||||
div.scripting-cell button.scripting-enabled {
|
||||
background-color: #339999;
|
||||
}
|
@ -192,7 +192,7 @@ export class Environment {
|
||||
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("$$")) {
|
||||
if (value == null && fullkey.length == 0 && !key.startsWith("$")) {
|
||||
this.error(key, `"${key}" has no value.`)
|
||||
}
|
||||
fullkey.push(key);
|
||||
@ -244,10 +244,22 @@ export class Environment {
|
||||
start: loc.column,
|
||||
}]
|
||||
}
|
||||
// TODO: Cannot parse given Error object
|
||||
// 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')) {
|
||||
@ -261,6 +273,7 @@ export class Environment {
|
||||
}
|
||||
--frame;
|
||||
}
|
||||
// if no stack frames parsed, last resort error msg
|
||||
if (errors.length == 0) {
|
||||
errors.push( {
|
||||
path: this.path,
|
||||
|
@ -4,7 +4,7 @@ import * as fastpng from 'fast-png';
|
||||
import { Palette } from './color';
|
||||
import * as io from './io'
|
||||
import * as color from './color'
|
||||
import { findIntegerFactors, RGBA } from '../../util';
|
||||
import { coerceToArray, findIntegerFactors, RGBA } from '../../util';
|
||||
|
||||
export type PixelMapFunction = (x: number, y: number) => number;
|
||||
|
||||
@ -17,17 +17,16 @@ export abstract class AbstractBitmap<T> {
|
||||
public readonly height: number,
|
||||
) {
|
||||
}
|
||||
|
||||
abstract blank(width: number, height: number) : AbstractBitmap<T>;
|
||||
abstract setarray(arr: ArrayLike<number>) : AbstractBitmap<T>;
|
||||
abstract set(x: number, y: number, val: number) : AbstractBitmap<T>;
|
||||
abstract setarray(arr: ArrayLike<number>) : 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<number> | PixelMapFunction) {
|
||||
assign(fn: ArrayLike<number> | PixelMapFunction) : void {
|
||||
if (typeof fn === 'function') {
|
||||
for (let y=0; y<this.height; y++) {
|
||||
for (let x=0; x<this.width; x++) {
|
||||
@ -39,10 +38,11 @@ export abstract class AbstractBitmap<T> {
|
||||
} else {
|
||||
throw new Error(`Illegal argument to assign(): ${fn}`)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
clone() : AbstractBitmap<T> {
|
||||
return this.blank(this.width, this.height).assign((x,y) => this.get(x,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);
|
||||
@ -64,6 +64,13 @@ export abstract class AbstractBitmap<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
fill(destx: number, desty: number, width:number, height:number, value:number) {
|
||||
for (var y=0; y<height; y++) {
|
||||
for (var x=0; x<width; x++) {
|
||||
this.set(x+destx, y+desty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RGBABitmap extends AbstractBitmap<RGBABitmap> {
|
||||
@ -80,11 +87,9 @@ export class RGBABitmap extends AbstractBitmap<RGBABitmap> {
|
||||
}
|
||||
setarray(arr: ArrayLike<number>) {
|
||||
this.rgba.set(arr);
|
||||
return this;
|
||||
}
|
||||
set(x: number, y: number, rgba: number) {
|
||||
if (this.inbounds(x,y)) this.rgba[y * this.width + x] = rgba;
|
||||
return this;
|
||||
}
|
||||
get(x: number, y: number): number {
|
||||
return this.inbounds(x,y) ? this.rgba[y * this.width + x] : 0;
|
||||
@ -92,8 +97,8 @@ export class RGBABitmap extends AbstractBitmap<RGBABitmap> {
|
||||
getrgba(x: number, y: number): number {
|
||||
return this.get(x, y);
|
||||
}
|
||||
blank(width: number, height: number) : RGBABitmap {
|
||||
return new RGBABitmap(width, height);
|
||||
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);
|
||||
@ -108,47 +113,55 @@ export abstract class MappedBitmap extends AbstractBitmap<MappedBitmap> {
|
||||
constructor(
|
||||
width: number,
|
||||
height: number,
|
||||
public readonly bitsPerPixel: number,
|
||||
public readonly bpp: number,
|
||||
initial?: Uint8Array | PixelMapFunction
|
||||
) {
|
||||
super(width, height);
|
||||
if (bitsPerPixel != 1 && bitsPerPixel != 2 && bitsPerPixel != 4 && bitsPerPixel != 8)
|
||||
throw new Error(`Invalid bits per pixel: ${bitsPerPixel}`);
|
||||
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<number>) {
|
||||
this.pixels.set(arr);
|
||||
return this;
|
||||
}
|
||||
set(x: number, y: number, index: number) {
|
||||
if (this.inbounds(x,y)) this.pixels[y * this.width + x] = index;
|
||||
return this;
|
||||
}
|
||||
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,
|
||||
bitsPerPixel: number,
|
||||
bppOrPalette: number | Palette,
|
||||
initial?: Uint8Array | PixelMapFunction
|
||||
) {
|
||||
super(width, height, bitsPerPixel || 8, initial);
|
||||
this.palette = color.palette.colors(1 << this.bitsPerPixel);
|
||||
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) : IndexedBitmap {
|
||||
let bitmap = new IndexedBitmap(width, height, this.bitsPerPixel);
|
||||
bitmap.palette = this.palette;
|
||||
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 {
|
||||
@ -184,6 +197,7 @@ export interface BitmapAnalysis {
|
||||
}
|
||||
|
||||
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;
|
||||
@ -202,6 +216,7 @@ export interface MontageOptions {
|
||||
}
|
||||
|
||||
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 = [];
|
||||
|
@ -1,12 +1,15 @@
|
||||
|
||||
import { FileDataCache } from "../../util";
|
||||
import { FileData, WorkingStore } from "../../workertypes";
|
||||
|
||||
// remote resource cache
|
||||
var $$cache: WeakMap<object,FileData> = new WeakMap();
|
||||
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<string,{}> = new Map();
|
||||
|
||||
export function $$setupFS(store: WorkingStore) {
|
||||
$$store = store;
|
||||
@ -38,7 +41,7 @@ export namespace data {
|
||||
return object;
|
||||
}
|
||||
export function save(object: Loadable, key: string): Loadable {
|
||||
if ($$data && object.$$getstate) {
|
||||
if ($$data && object && object.$$getstate) {
|
||||
$$data[key] = object.$$getstate();
|
||||
}
|
||||
return object;
|
||||
@ -67,10 +70,6 @@ export function canonicalurl(url: string) : string {
|
||||
return url;
|
||||
}
|
||||
|
||||
export function clearcache() {
|
||||
$$cache = new WeakMap();
|
||||
}
|
||||
|
||||
export function fetchurl(url: string, type?: 'binary' | 'text'): FileData {
|
||||
// TODO: only works in web worker
|
||||
var xhr = new XMLHttpRequest();
|
||||
@ -99,17 +98,19 @@ export function readnocache(url: string, type?: 'binary' | 'text'): FileData {
|
||||
|
||||
// TODO: read files too
|
||||
export function read(url: string, type?: 'binary' | 'text'): FileData {
|
||||
// canonical-ize url
|
||||
url = canonicalurl(url);
|
||||
// check cache
|
||||
let cachekey = {url: url};
|
||||
if ($$cache.has(cachekey)) {
|
||||
return $$cache.get(cachekey);
|
||||
}
|
||||
let data = readnocache(url, type);
|
||||
// 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`);
|
||||
$$cache.set(cachekey, data);
|
||||
// store in cache
|
||||
$$cache.put(cachekey, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -129,6 +130,22 @@ 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<T> implements Loadable {
|
||||
|
@ -1,6 +1,10 @@
|
||||
|
||||
import { coerceToArray } from "../../util";
|
||||
import * as io from "./io";
|
||||
|
||||
// sequence counter
|
||||
var $$seq : number = 0;
|
||||
|
||||
// if an event is specified, it goes here
|
||||
export const EVENT_KEY = "$$event";
|
||||
|
||||
@ -17,17 +21,22 @@ export interface InteractEvent {
|
||||
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(
|
||||
public readonly interacttarget: Interactive,
|
||||
private $$callback
|
||||
interacttarget: Interactive,
|
||||
private $$callback: InteractCallback
|
||||
) {
|
||||
this.interacttarget = interacttarget || (<any>this as Interactive);
|
||||
this.interactid = ++$$seq;
|
||||
}
|
||||
$$setstate(newstate: {interactid: number}) {
|
||||
this.interactid = newstate.interactid;
|
||||
@ -46,7 +55,7 @@ export class InteractionRecord implements io.Loadable {
|
||||
//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 this;
|
||||
return {interactid: this.interactid};
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,17 +134,42 @@ export function select(options: any[]) {
|
||||
|
||||
///
|
||||
|
||||
export class ScriptUIButtonType implements ScriptUIType {
|
||||
export class ScriptUIButtonType extends InteractionRecord implements ScriptUIType, Interactive {
|
||||
readonly uitype = 'button';
|
||||
$$interact: InteractionRecord;
|
||||
enabled?: boolean;
|
||||
|
||||
constructor(
|
||||
readonly name: string
|
||||
readonly label: string,
|
||||
callback: InteractCallback
|
||||
) {
|
||||
super(null, callback);
|
||||
this.$$interact = this;
|
||||
}
|
||||
}
|
||||
|
||||
export class ScriptUIButton extends ScriptUIButtonType {
|
||||
}
|
||||
|
||||
export function button(name: string) {
|
||||
return new ScriptUIButton(name);
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
@ -12,27 +12,27 @@ import * as scriptui from "../lib/scriptui";
|
||||
const MAX_STRING_LEN = 100;
|
||||
const DEFAULT_ASPECT = 1;
|
||||
|
||||
interface ObjectStats {
|
||||
type: 'prim' | 'complex' | 'bitmap'
|
||||
width: number
|
||||
height: number
|
||||
units: 'em' | 'px'
|
||||
}
|
||||
|
||||
// TODO
|
||||
class ObjectAnalyzer {
|
||||
recurse(obj: any) : ObjectStats {
|
||||
if (typeof obj === 'string') {
|
||||
return { type: 'prim', width: obj.length, height: 1, units: 'em' }
|
||||
} else if (obj instanceof Uint8Array) {
|
||||
return { type: 'complex', width: 60, height: Math.ceil(obj.length / 16), units: 'em' }
|
||||
} else if (typeof obj === 'object') {
|
||||
let stats : ObjectStats = { type: 'complex', width: 0, height: 0, units: 'em'};
|
||||
return stats; // TODO
|
||||
} else {
|
||||
return { type: 'prim', width: 12, height: 1, units: 'em' }
|
||||
}
|
||||
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 {
|
||||
@ -56,27 +56,6 @@ class ColorComponent extends Component<ColorComponentProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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.round(x);
|
||||
ievent.y = Math.round(y);
|
||||
// TODO: pressure, etc.
|
||||
}
|
||||
// TODO: add events to queue?
|
||||
current_project.updateDataItems([{
|
||||
key: scriptui.EVENT_KEY,
|
||||
value: ievent
|
||||
}]);
|
||||
}
|
||||
|
||||
interface BitmapComponentProps {
|
||||
bitmap: bitmap.BitmapType;
|
||||
width: number;
|
||||
@ -253,7 +232,7 @@ function primitiveToString(obj) {
|
||||
}
|
||||
|
||||
function isIndexedBitmap(object): object is bitmap.IndexedBitmap {
|
||||
return object['bitsPerPixel'] && object['pixels'] && object['palette'];
|
||||
return object['bpp'] && object['pixels'] && object['palette'];
|
||||
}
|
||||
function isRGBABitmap(object): object is bitmap.RGBABitmap {
|
||||
return object['rgba'] instanceof Uint32Array;
|
||||
@ -275,7 +254,10 @@ function objectToDiv(object: any, name: string, objpath: string): VNode<any> {
|
||||
// don't view any keys that start with "$"
|
||||
if (name && name.startsWith("$")) {
|
||||
// don't view any values that start with "$$"
|
||||
if (name.startsWith("$$")) { return; }
|
||||
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
|
||||
@ -400,9 +382,24 @@ class UISelectComponent extends Component<UIComponentProps> {
|
||||
}
|
||||
}
|
||||
|
||||
class UIButtonComponent extends Component<UIComponentProps> {
|
||||
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
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
const UI_COMPONENTS = {
|
||||
'slider': UISliderComponent,
|
||||
'select': UISelectComponent,
|
||||
'button': UIButtonComponent,
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { UintArray } from "../ide/pixeleditor";
|
||||
|
||||
export function lpad(s:string, n:number):string {
|
||||
s += ''; // convert to string
|
||||
@ -56,7 +55,7 @@ export function toradix(v:number, nd:number, radix:number) {
|
||||
}
|
||||
}
|
||||
|
||||
export function arrayCompare(a:any[]|UintArray, b:any[]|UintArray):boolean {
|
||||
export function arrayCompare(a:ArrayLike<any>, b:ArrayLike<any>):boolean {
|
||||
if (a == null && b == null) return true;
|
||||
if (a == null) return false;
|
||||
if (b == null) return false;
|
||||
@ -662,3 +661,34 @@ export function findIntegerFactors(x: number, mina: number, minb: number, aspect
|
||||
}
|
||||
return {a, b};
|
||||
}
|
||||
|
||||
export class FileDataCache {
|
||||
maxSize : number = 8000000;
|
||||
size : number;
|
||||
cache : Map<string, string|Uint8Array>;
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
get(key : string) : string|Uint8Array {
|
||||
return this.cache.get(key);
|
||||
}
|
||||
put(key : string, value : string|Uint8Array) {
|
||||
this.cache.set(key, value);
|
||||
this.size += value.length;
|
||||
if (this.size > this.maxSize) {
|
||||
console.log('cache reset', this);
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
this.cache = new Map();
|
||||
this.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function coerceToArray<T>(arrobj: any) : T[] {
|
||||
if (Array.isArray(arrobj)) return arrobj;
|
||||
else if (arrobj != null && typeof arrobj[Symbol.iterator] === 'function') return Array.from(arrobj);
|
||||
else if (typeof arrobj === 'object') return Array.from(Object.values(arrobj))
|
||||
else throw new Error(`Expected array or object, got "${arrobj}"`);
|
||||
}
|
||||
|
@ -147,6 +147,7 @@ function fatalError(s:string) {
|
||||
}
|
||||
|
||||
function newWorker() : Worker {
|
||||
// TODO: return new Worker("https://8bitworkshop.com.s3-website-us-east-1.amazonaws.com/dev/gen/worker/bundle.js");
|
||||
return new Worker("./gen/worker/bundle.js");
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { PLATFORMS, RasterVideo } from "../common/emu";
|
||||
import { PLATFORMS } from "../common/emu";
|
||||
import { Platform } from "../common/baseplatform";
|
||||
import { RunResult } from "../common/script/env";
|
||||
import { Notebook } from "../common/script/ui/notebook";
|
||||
|
Loading…
x
Reference in New Issue
Block a user