From 7a6c26284a489b38cfe956a130b112428a2872c0 Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Thu, 14 Jul 2011 17:20:49 -0400 Subject: [PATCH] keeping the implementation from js-vm; we're going to adapt it to whalesong --- .../jsworld/compiled/define-effect_rkt.dep | 1 + .../jsworld/compiled/define-effect_rkt.zo | Bin 0 -> 5939 bytes .../scratch/jsworld/compiled/jsworld_rkt.dep | 1 + world/scratch/jsworld/compiled/jsworld_rkt.zo | Bin 0 -> 97523 bytes world/scratch/jsworld/define-effect.rkt | 82 + world/scratch/jsworld/jsworld.js | 1396 ++++++++++++ world/scratch/jsworld/jsworld.rkt | 50 + world/scratch/jsworld/private/jsworld.js | 777 +++++++ .../jsworld/private/jsworld/jsworld.js | 1488 +++++++++++++ world/scratch/world/compiled/kernel_rkt.dep | 1 + world/scratch/world/compiled/kernel_rkt.zo | Bin 0 -> 56139 bytes world/scratch/world/kernel.js | 1938 +++++++++++++++++ world/scratch/world/kernel.rkt | 3 + 13 files changed, 5737 insertions(+) create mode 100644 world/scratch/jsworld/compiled/define-effect_rkt.dep create mode 100644 world/scratch/jsworld/compiled/define-effect_rkt.zo create mode 100644 world/scratch/jsworld/compiled/jsworld_rkt.dep create mode 100644 world/scratch/jsworld/compiled/jsworld_rkt.zo create mode 100644 world/scratch/jsworld/define-effect.rkt create mode 100644 world/scratch/jsworld/jsworld.js create mode 100644 world/scratch/jsworld/jsworld.rkt create mode 100644 world/scratch/jsworld/private/jsworld.js create mode 100644 world/scratch/jsworld/private/jsworld/jsworld.js create mode 100644 world/scratch/world/compiled/kernel_rkt.dep create mode 100644 world/scratch/world/compiled/kernel_rkt.zo create mode 100644 world/scratch/world/kernel.js create mode 100644 world/scratch/world/kernel.rkt diff --git a/world/scratch/jsworld/compiled/define-effect_rkt.dep b/world/scratch/jsworld/compiled/define-effect_rkt.dep new file mode 100644 index 0000000..9caa623 --- /dev/null +++ b/world/scratch/jsworld/compiled/define-effect_rkt.dep @@ -0,0 +1 @@ +("5.1.1" ("7f57f25a3f0077a7b31ba24f7b5dbfc789ba7d1e" . "ff5c04401e157808d662a002dabdfdb12aa66314") (collects #"racket" #"base.rkt") #"/home/dyoo/work/js-vm/jsworld/../lang/base.rkt" #"/home/dyoo/work/js-vm/jsworld/jsworld.rkt" (collects #"s-exp" #"lang" #"reader.rkt")) diff --git a/world/scratch/jsworld/compiled/define-effect_rkt.zo b/world/scratch/jsworld/compiled/define-effect_rkt.zo new file mode 100644 index 0000000000000000000000000000000000000000..ba698836d17507f771f07e6685152319085142c4 GIT binary patch literal 5939 zcmcgwX>c4z6`pysmU?%z(&*ZiCCm22yQ{;Ry;qkH9hNQIF|mn55(it-NISANTJ5fP zMz$P+5<)JVn>d(YkSvp17@*0;3K#UoFED-gs{*m^ay>z^};@3OwfgC z;XdIJ;h1nz_>u6ka7wr!yeYgVd?@@y_@}_zN^EZ1H8#c8Zrf|S$L3u2963)uA{Xp` zvpXDh4xc0F*y9*>Jm8ph{MB)d*eVW)S@9Y1CGlgCFR3W`X^GG2ceXmaohO|yIbU`D z+4(Q$Cr(f4w$fDT`M(L_p9w9!#* z#Hbg=J|lZrNoi9?cScQQyN460c%PO`CNjFF_Z*l^8=B3Qn8+kom0zLh&yE{>jo zMrTT$&^Aw~(%BshY7SE|4rjD(D&4G4X0$A= z_KzmCWLz0ZPo|7+I>26Ye0@1CNias)?O)x}# zk==ga0Jc+jeNkCiDxIBB%wd^L4#&b;RaLGJ)XAt>bt0$e_XnumwOgxPgwx^ehtVj%&J+ugBW*Y-@pSE3oYawj-9q#)6SN8w%#xNb5z{QJecU zn0IZsLT87uv}BFmdDnK2PNmv_xJd3@Eed?SwxtTmCp$|D6~TJ2&h_+}mzY++vcdhwDt4CS-Wr3a{}Z-4sk3$w?!NNuxPw^ixvoDAw$A|Aw!t z64I<0y{@^SgYzzxpA zMvrF}x~xJ1-~m*xCEhm+F^?A;H_QQeVFMti`W6ViUWpqLtpYEL1BBM}lhk+*Y7d^J*)xw2~GGg6XXe<(z z@Ic;V8S6DK+?|uD(WGiDG^6*LcQ9OMKFqCb{whUF;V88PTVWetwTQkB+u;l5!*rAB zbf(~2km`1++5O#FJ!x!PI5vGpGGSl_iUY)aW+EN$f*s~Vr-Al|manCcREjnZjivk1 z4#i?h($%d^WwKr4hxERjl}#f_brREd=r26fqYcTtJHaq(uo4;^TQErX(AoB!Te}Zsa&JwvT+g9s`9=JM1#o0rSX67y-T5u3`XD3Si=Z?OJx) zdSDfN(R`RTMjiI9EsRETJNXJ45+5@okx~G*S?O`Tl!5CD;Y{uz(`cZ~GC(N+>*cc` z$pTSoffgJLmMi5Jh|(oNH5Q1{B|)n!5T#3kd=`k(s~q`#K<)(``&!EcrB*)+$DV_= zSTIVLbjNRjD7{K|9t83Lb*IBJLFppy_N9~!+vm^&ZrJ6 zT`^xF3h9dVGBE>HP(h1Iaun!gMHN2}2XD6=9CBc`wARd)T4=VUkZ&*}+-)4?b09)B1@Zw=Lg;hGy8?5^^9E*h_IOBI%YPQu4y{)=LR2c;6is1_2B*Gn5r5S8V5iO2U@{-&YbGad=>O~FUJAbh~d1G{ED zm~mRoiZ}G>_XorbQcUB2#|%+SgBLA(*Wv4&d$ISyj?N(16%XF8Px>8bSEWO0ki?Y` zhKNARD6Fh6A|#p64VJ02ZW*3T;Kw}Lz@v!1+Gg07Bi?O&HOqbJ%49W#eo@2BQ_Y0& zji!AwCR0qa%bu%kq=uHQgiv2r!%tcG6^^6bcJQ?9M1I{4^V1UhZpCt2fpwev*3{ac zh-)b$fu9DmU4m36@0V|rzbfAe^4CDV3*@hZd^gDVfP6p5-vIdt$Pa=1FvyR9{7sM_ p2l)w*kAeIo$ln3^yCBbid;;V-ke>$m8Nk&VfG*g$4`Oz^=RXRWy)^&; literal 0 HcmV?d00001 diff --git a/world/scratch/jsworld/compiled/jsworld_rkt.dep b/world/scratch/jsworld/compiled/jsworld_rkt.dep new file mode 100644 index 0000000..556edb6 --- /dev/null +++ b/world/scratch/jsworld/compiled/jsworld_rkt.dep @@ -0,0 +1 @@ +("5.1.1" ("95136ed093c9b56fe70118d153ccf5c246e76401" . "61cbeb742f46f0d5b7bd52121d6f3b0a872cdfeb") #"/home/dyoo/work/js-vm/jsworld/../lang/js-impl/js-impl.rkt" #"/home/dyoo/work/js-vm/jsworld/../world/kernel.rkt" #"/home/dyoo/work/js-vm/jsworld/../image/image.rkt" (collects #"s-exp" #"lang" #"reader.rkt")) diff --git a/world/scratch/jsworld/compiled/jsworld_rkt.zo b/world/scratch/jsworld/compiled/jsworld_rkt.zo new file mode 100644 index 0000000000000000000000000000000000000000..c18cd47c0803d5222b73da94e3e8bf5734f2d785 GIT binary patch literal 97523 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 #'(field ...)))] + [struct:name + (datum->syntax #'name + (string->symbol + (format "struct:~a" + (syntax-e #'name))))] + [make-name + (datum->syntax #'name + (string->symbol + (format "make-~a" + (syntax-e #'name))))] + [name? + (datum->syntax #'name + (string->symbol + (format "~a?" + (syntax-e #'name))))] + [(field-index ...) + (build-list (length (syntax->list + #'(field ...))) + (lambda (i) i))] + [(accessor ...) + (map (lambda (field) + (datum->syntax + field + (string->symbol + (format "~a-~a" + (syntax-e #'name) + (syntax-e field))))) + (syntax->list #'(field ...)))] + + [(mutator ...) + (map (lambda (field) + (datum->syntax + field + (string->symbol + (format "set-~a-~a!" + (syntax-e #'name) + (syntax-e field))))) + (syntax->list #'(field ...)))]) + + (syntax/loc stx + (begin (define-values (struct:name + make-name + name? + name-accessor + name-mutator) + (make-effect-type 'name + supertype + field-count + impl)) + (begin + (define accessor + (make-struct-field-accessor + name-accessor field-index 'field)) + ...) + + (begin + (define mutator + (make-struct-field-mutator + name-mutator field-index 'field)) + ...) + + )))])) + \ No newline at end of file diff --git a/world/scratch/jsworld/jsworld.js b/world/scratch/jsworld/jsworld.js new file mode 100644 index 0000000..748ee1e --- /dev/null +++ b/world/scratch/jsworld/jsworld.js @@ -0,0 +1,1396 @@ + +/************************ + *** World Primitives *** + ************************/ + +var PrimProc = types.PrimProc; +var CasePrimitive = types.CasePrimitive; +var makeOptionPrimitive = types.makeOptionPrimitive; +var checkListOf = helpers.checkListOf; +var procArityContains = helpers.procArityContains; +var raise = helpers.raise; + + +var makeCaller = function(aState) { + return function(operator, operands, k, callSite) { + interpret.call(aState, operator, operands, k, aState.onFail, callSite); + }; +}; + + + + +// Every world configuration function (on-tick, stop-when, ...) +// produces a WorldConfigOption instance. +var WorldConfigOption = function(name) { + this.name = name; +}; + +WorldConfigOption.prototype.configure = function(config) { + raise(types.incompleteExn( + types.exnFailContract, + 'unimplemented WorldConfigOption', + [])); +}; + +WorldConfigOption.prototype.toDomNode = function(cache) { + var div = document.createElement('div'); + div.appendChild(document.createTextNode("(" + this.name + " ...)")); + return div; +}; + +WorldConfigOption.prototype.toWrittenString = function(cache) { + return "(" + this.name + " ...)"; +}; + +WorldConfigOption.prototype.toDisplayedString = function(cache) { + return "(" + this.name + " ...)"; +}; + + + +var isWorldConfigOption = function(x) { return x instanceof WorldConfigOption; }; + + + + + +// convertAttribList: (listof (list string (or string boolean))) -> (hashof string string) +var convertAttribList = function(attribList) { + var newList = types.EMPTY; + var nextElt; + var key, val; + while (!types.isEmpty(attribList)) { + nextElt = attribList.first(); + + key = nextElt.first(); + val = nextElt.rest().first(); + + key = String(key); + + if (types.isString(val)) { + val = String(val); + } else if (types.isBoolean(val)) { + // do nothing: the representation is the same. + } else if (types.isSymbol(val)) { + if (String(val) === 'true') { + val = true; + } else if (String(val) === 'false') { + val = false; + } else { + val = String(val); + } + } else { + // raise error: neither string nor boolean + raise(types.incompleteExn( + types.exnFailContract, + helpers.format( + "attribute value ~s neither a string nor a boolean", + [val]), + [])); + } + // ensure each element in the hash are primitive strings + newList = types.cons(types.list([key, val]), + newList); + attribList = attribList.rest(); + } + return helpers.assocListToHash(newList); +} + + + + +////////////////////////////////////////////////////////////////////// + + + + +EXPORTS['key=?'] = + new PrimProc('key=?', + 2, + false, false, + function(key1, key2) { + return (String(key1).toLowerCase() === + String(key2).toLowerCase()); + }); + + + + + +var OnTickBang = function(handler, effectHandler, aDelay) { + WorldConfigOption.call(this, 'on-tick'); + this.handler = handler; + this.effectHandler = effectHandler; + this.aDelay = aDelay; +}; + +OnTickBang.prototype = helpers.heir(WorldConfigOption.prototype); + +OnTickBang.prototype.configure = function(config) { + var newVals = { + onTick: this.handler, + onTickEffect: this.effectHandler, + tickDelay: jsnums.toFixnum(jsnums.multiply(1000, this.aDelay)) + }; + return config.updateAll(newVals); +}; + + + + +// The default tick delay is 28 times a second. +var DEFAULT_TICK_DELAY = types.rational(1, 28); + +EXPORTS['on-tick'] = + new CasePrimitive( + 'on-tick', + [new PrimProc('on-tick', + 1, + false, false, + function(f) { + check(f, isFunction, "on-tick", "procedure", 1); + return new OnTickBang(f, + new PrimProc('', 1, false, false, + function(w) { return types.effectDoNothing(); }), + DEFAULT_TICK_DELAY); + }), + new PrimProc('on-tick', + 2, + false, false, + function(f, aDelay) { + check(f, isFunction, "on-tick", "procedure", 1, arguments); + check(aDelay, isNumber, "on-tick", "number", 2, arguments); + return new OnTickBang(f, + new PrimProc('', 1, false, false, + function(w) { return types.effectDoNothing(); }), + aDelay); + }) ]); + + + +EXPORTS['on-tick!'] = + new CasePrimitive('on-tick!', + [new PrimProc('on-tick!', + 2, + false, false, + function(handler, effectHandler) { + check(handler, isFunction, "on-tick!", "procedure", 1, arguments); + check(effectHandler, isFunction, "on-tick!","procedure", 2, arguments); + return new OnTickBang(handler, effectHandler, DEFAULT_TICK_DELAY); + }), + new PrimProc('on-tick!', + 3, + false, false, + function(handler, effectHandler, aDelay) { + check(handler, isFunction, "on-tick!", "procedure", 1, arguments); + check(effectHandler, isFunction, "on-tick!","procedure", 2, arguments); + check(aDelay, isNumber, "on-tick!", "number", 3, arguments); + return new OnTickBang(handler, effectHandler, aDelay); + }) ]); + + + +var onEvent = function(funName, inConfigName, numArgs) { + return function(handler) { + return onEventBang(funName, inConfigName)(handler, + new PrimProc('', numArgs, false, false, function() { return types.EMPTY; })); + }; +}; + + +var onEventBang = function(funName, inConfigName) { + + var CustomConfigOption = function(handler, effectHandler) { + WorldConfigOption.call(this, funName); + this.handler = handler; + this.effectHandler = effectHandler; + }; + CustomConfigOption.prototype = helpers.heir(WorldConfigOption.prototype); + + CustomConfigOption.prototype.configure =function(config) { + var newHash = {}; + newHash[inConfigName] = this.handler; + newHash[inConfigName+'Effect'] = this.effectHandler; + return config.updateAll(newHash); + } + + return function(handler, effectHandler) { + check(handler, isFunction, funName, 'procedure', 1, arguments); + check(effectHandler, isFunction, funName, 'procedure', 2, arguments); + return new CustomConfigOption(handler, effectHandler); + }; +}; + + +EXPORTS['on-key'] = new PrimProc('on-key', 1, false, false, onEvent('on-key', 'onKey', 2)); +EXPORTS['on-key!'] = new PrimProc('on-key!', 2, false, false, onEventBang('on-key!', 'onKey')); + + +EXPORTS['stop-when'] = new PrimProc('stop-when', 1, false, false, + onEvent('stop-when', 'stopWhen', 1)); +EXPORTS['stop-when!'] = new PrimProc('stop-when!', 2, false, false, + onEventBang('stop-when!', 'stopWhen')); + + + + + +var DrawConfigOption = function(f) { + WorldConfigOption.call(this, 'to-draw'); + this.f = f; +}; + +DrawConfigOption.prototype = helpers.heir(WorldConfigOption.prototype); + +DrawConfigOption.prototype.configure = function(config) { + return config.updateAll({'onRedraw': this.f}); +}; + + +EXPORTS['to-draw'] = + new PrimProc('to-draw', + 1, + false, false, + function(f) { + check(f, isFunction, 'to-draw', 'procedure', 1); + return new DrawConfigOption(f); + }); + + +var DrawPageOption = function(domHandler) { + WorldConfigOption.call(this, 'to-draw-page'); + this.domHandler = domHandler; +}; +DrawPageOption.prototype = helpers.heir(WorldConfigOption.prototype); +DrawPageOption.prototype.configure = function(config) { + return config.updateAll({'onDraw': this.domHandler}); +}; + + +var DrawPageAndCssOption = function(domHandler, styleHandler) { + WorldConfigOption.call(this, 'to-draw-page'); + this.domHandler = domHandler; + this.styleHandler = styleHandler; +}; +DrawPageAndCssOption.prototype = helpers.heir(WorldConfigOption.prototype); +DrawPageAndCssOption.prototype.configure = function(config) { + return config.updateAll({'onDraw': this.domHandler, + 'onDrawCss' : this.styleHandler}); +}; + + + + +EXPORTS['to-draw-page'] = + new CasePrimitive('to-draw-page', + [new PrimProc('to-draw-page', + 1, + false, false, + function(domHandler) { + check(domHandler, isFunction, 'to-draw-page', 'procedure', 1); + return new DrawPageOption(domHandler); + }), + new PrimProc('to-draw-page', + 2, + false, false, + function(domHandler, styleHandler) { + check(domHandler, isFunction, 'to-draw-page', 'procedure', 1, arguments); + check(styleHandler, isFunction, 'to-draw-page', 'procedure', 2, arguments); + return new DrawPageAndCssOption(domHandler, styleHandler); }) ]); + + +var InitialEffectOption = function(effect) { + WorldConfigOption.call(this, 'initial-effect'); + this.effect = effect; +}; +InitialEffectOption.prototype = helpers.heir(WorldConfigOption.prototype); +InitialEffectOption.prototype.configure = function(config) { + return config.updateAll({'initialEffect': this.effect}); +}; + + +EXPORTS['initial-effect'] = + new PrimProc('initial-effect', + 1, + false, false, + function(effect) { + return new InitialEffectOption(effect); + }); + + + +/************************** + *** Jsworld Primitives *** + **************************/ + + +var jsp = function(attribList) { + checkListOf(attribList, function(x) { return isList(x) && length(x) == 2; }, + 'js-p', 'list of (list of X Y)', 1); + var attribs = convertAttribList(attribList); + var node = jsworld.MobyJsworld.p(attribs); + node.toWrittenString = function(cache) { return "(js-p)"; }; + node.toDisplayedString = node.toWrittenString; + node.toDomNode = function(cache) { return node; }; + return helpers.wrapJsValue(node); +}; +EXPORTS['js-p'] = + new CasePrimitive('js-p', + [new PrimProc('js-p', 0, false, false, function() { return jsp(types.EMPTY); }), + new PrimProc('js-p', 1, false, false, jsp)]); + + +var jsdiv = function(attribList) { + checkListOf(attribList, isAssocList, 'js-div', '(listof X Y)', 1); + + var attribs = convertAttribList(attribList); + var node = jsworld.MobyJsworld.div(attribs); + + node.toWrittenString = function(cache) { return "(js-div)"; }; + node.toDisplayedString = node.toWrittenString; + node.toDomNode = function(cache) { return node; }; + return helpers.wrapJsValue(node); +}; + +EXPORTS['js-div'] = + new CasePrimitive('js-div', + [new PrimProc('js-div', 0, false, false, function() { + return jsdiv(types.EMPTY); + }), + new PrimProc('js-div', 1, false, false, jsdiv) + ]); + + +var jsButtonBang = function(funName) { + return function(worldUpdateF, effectF, attribList) { + check(worldUpdateF, isFunction, funName, 'procedure', 1); + check(effectF, isFunction, funName, 'procedure', 2); + checkListOf(attribList, isAssocList, funName, '(listof X Y)', 3); + + var attribs = attribList ? convertAttribList(attribList) : {}; + var node = jsworld.MobyJsworld.buttonBang(worldUpdateF, effectF, attribs); + + node.toWrittenString = function(cache) { return '(' + funName + ' ...)'; }; + node.toDisplayedString = node.toWrittenString; + node.toDomNode = function(cache) { return node; }; + return helpers.wrapJsValue(node); + } +}; +var jsButton = function(updateWorldF, attribList) { + var noneF = new types.PrimProc('', 1, false, false, function(w) { return types.EMPTY; }); + return jsButtonBang('js-button')(updateWorldF, noneF, attribList); +}; +EXPORTS['js-button'] = + new CasePrimitive('js-button', + [new PrimProc('js-button', 1, false, false, + function(updateWorldF) { + return jsButton(updateWorldF, types.EMPTY)}), + new PrimProc('js-button', 2, false, false, jsButton)]); + + +EXPORTS['js-button!'] = + new CasePrimitive('js-button!', + [new PrimProc('js-button!', 2, false, false, + function(worldUpdateF, effectF) { + return jsButtonBang('js-button!')(worldUpdateF, effectF, types.EMPTY); + }), + new PrimProc('js-button!', 3, false, false, + jsButtonBang('js-button!'))]); + + + +var jsInput = function(type, updateF, attribList) { + check(type, isString, 'js-input', 'string', 1); + check(updateF, isFunction, 'js-input', 'procedure', 2); + checkListOf(attribList, isAssocList, 'js-input', '(listof X Y)', 3); + + var attribs = attribList ? convertAttribList(attribList) : {}; + var node = jsworld.MobyJsworld.input(String(type), + updateF, attribs); + + node.toWrittenString = function(cache) { return "(js-input ...)"; } + node.toDisplayedString = node.toWrittenString; + node.toDomNode = function(cache) { return node; } + return helpers.wrapJsValue(node); +}; + +EXPORTS['js-input'] = + new CasePrimitive('js-input', + [new PrimProc('js-input', 2, false, false, + function(type, updateF) { + return jsInput(type, updateF, types.EMPTY)}), + new PrimProc('js-input', 3, false, false, jsInput)]); + + + +var jsImg = function(src, attribList) { + check(src, isString, "js-img", "string", 1); + checkListOf(attribList, isAssocList, 'js-img', '(listof X Y)', 2); + + var attribs = convertAttribList(attribList); + var node = jsworld.MobyJsworld.img(String(src), attribs); + + node.toWrittenString = function(cache) { return "(js-img ...)"; } + node.toDisplayedString = node.toWrittenString; + node.toDomNode = function(cache) { return node; } + return helpers.wrapJsValue(node); +}; + + + +EXPORTS['js-img'] = + new CasePrimitive('js-img', + [new PrimProc('js-img', 1, false, false, + function(src) { return jsImg(src, types.EMPTY); }), + new PrimProc('js-img', 2, false, false, jsImg)]); + + + +EXPORTS['js-text'] = + new PrimProc('js-text', + 1, + false, false, + function(s) { + check(s, isString, 'js-text', 'string', 1); + + var node = jsworld.MobyJsworld.text(String(s), []); + node.toWrittenString = function(cache) { return "(js-text ...)"; } + node.toDisplayedString = node.toWrittenString; + node.toDomNode = function(cache) { return node; } + return helpers.wrapJsValue(node); + }); + + +var jsSelect = function(optionList, updateF, attribList) { + checkListOf(optionList, isString, 'js-select', 'listof string', 1); + check(updateF, isFunction, 'js-select', 'procedure', 2); + checkListOf(attribList, isAssocList, 'js-select', '(listof X Y)', 3); + + var attribs = attribList ? convertAttribList(attribList) : {}; + var options = helpers.deepListToArray(optionList); + for (var i = 0 ; i < options.length; i++) { + options[i] = String(options[i]); + } + var node = jsworld.MobyJsworld.select(options, updateF, attribs); + + node.toWrittenString = function(cache) { return '(js-select ...)'; }; + node.toDisplayedString = node.toWrittenString; + node.toDomNode = function(cache) { return node; }; + return helpers.wrapJsValue(node); +}; + + +EXPORTS['js-select'] = + new CasePrimitive( + 'js-select', + [new PrimProc('js-select', 2, false, false, + function(optionList, updateF) { + return jsSelect(optionList, updateF, + types.EMPTY) + }), + new PrimProc('js-select', 3, false, false, + jsSelect)]); + + + + +EXPORTS['big-bang'] = + new PrimProc('big-bang', + 1, + true, true, + function(state, initW, handlers) { + arrayEach(handlers, + function(x, i) { + check(x, function(y) { return isWorldConfigOption(y) || isList(y) || types.isWorldConfig(y); }, + 'js-big-bang', 'handler or attribute list', i+2); + }); + var unwrappedConfigs = + helpers.map(function(x) { + if ( isWorldConfigOption(x) ) { + return function(config) { return x.configure(config); }; + } + else { + return x; + } + }, + handlers); + return types.internalPause(function(caller, restarter, onFail) { + var bigBangController; + var onBreak = function() { + bigBangController.breaker(); + } + state.addBreakRequestedListener(onBreak); + bigBangController = jsworld.MobyJsworld.bigBang( + initW, + state.getToplevelNodeHook()(), + unwrappedConfigs, + caller, + function(v) { + state.removeBreakRequestedListener(onBreak); + restarter(v); + }, + onFail); + }) + }); + + +////////////////////////////////////////////////////////////////////// + + +var emptyPage = function(attribList) { + checkListOf(attribList, isAssocList, 'empty-page', '(listof X Y)', 1); + + var attribs = convertAttribList(attribList); + var node = jsworld.MobyJsworld.emptyPage(attribs); + + // node.toWrittenString = function(cache) { return "(js-div)"; }; + // node.toDisplayedString = node.toWrittenString; + // node.toDomNode = function(cache) { return node; }; + // return helpers.wrapJsValue(node); + return node; +}; + +EXPORTS['empty-page'] = + new CasePrimitive('empty-page', + [new PrimProc('empty-page', 0, false, false, + function() { return emptyPage(types.EMPTY); }), + new PrimProc('empty-page', 1, false, false, emptyPage)]); + + +EXPORTS['place-on-page'] = + new PrimProc('empty-page', + 4, + false, false, + function(elt, left, top, page) { + // FIXME: add type checking + check(left, isReal, 'place-on-page', 'real', 2); + check(top, isReal, 'place-on-page', 'real', 3); + return jsworld.MobyJsworld.placeOnPage( + elt, jsnums.toFixnum(left), jsnums.toFixnum(top), page); + }); + + + + + +////////////////////////////////////////////////////////////////////// + + + + + +EXPORTS['make-world-config'] = + new PrimProc('make-world-config', + 2, + true, false, + function(startup, shutdown, startupArgs) { + var allArgs = [startup, shutdown].concat(startupArgs); + check(startup, isFunction, 'make-world-config', 'procedure', 1, allArgs); + check(shutdown, procArityContains(1), 'make-world-config', 'procedure (arity 1)', 2, allArgs); + arrayEach(startupArgs, function(x, i) { check(x, isFunction, 'make-world-config', 'handler', i+3, allArgs); }); + + if ( !procArityContains(startupArgs.length)(startup) ) { + raise( types.incompleteExn( + types.exnFailContract, + 'make-world-config: 1st argument must have arity equal to ' + + 'the number of arguments after the second', + []) ); + } + + return types.worldConfig(startup, shutdown, startupArgs); + }); + + +EXPORTS['make-effect-type'] = + makeOptionPrimitive( + 'make-effect-type', + 4, + [false], + true, + function(userArgs, aState, name, superType, fieldCnt, impl, guard) { + check(name, isSymbol, 'make-effect-type', 'string', 1, userArgs); + check(superType, function(x) { return x === false || types.isEffectType(x) }, + 'make-effect-type', 'effect type or #f', 2, userArgs); + check(fieldCnt, isNatural, 'make-effect-type', 'exact non-negative integer', 3, userArgs); + check(impl, isFunction, 'make-effect-type', 'procedure', 4, userArgs); +// checkListOf(handlerIndices, isNatural, 'make-effect-type', 'exact non-negative integer', 5); + check(guard, function(x) { return x === false || isFunction(x); }, 'make-effect-type', 'procedure or #f', 6, userArgs); + // Check the number of arguments on the guard + var numberOfGuardArgs = fieldCnt + 1 + (superType ? superType.numberOfArgs : 0); + if ( guard && !procArityContains(numberOfGuardArgs)(guard) ) { + raise(types.incompleteExn( + types.exnFailContract, + helpers.format( + 'make-effect-type: guard procedure does not accept ~a arguments ' + + '(one more than the number constructor arguments): ~s', + [numberOfGuardArgs, guard]), + [])); + } + +// var jsImpl = schemeProcToJs(aState, impl); + var jsGuard = (guard ? schemeProcToJs(aState, guard) : false); +// var handlerIndices_js = helpers.map(jsnums.toFixnum, helpers.schemeListToArray(handlerIndices)); + +// var caller = makeCaller(aState); +// var wrapHandler = function(handler, changeWorld) { +// return types.jsObject('function', function() { +// var externalArgs = arguments; +// changeWorld(function(w, k) { +// var args = [w]; +// for (var i = 0; i < externalArgs.length; i++) { +// args.push( helpers.wrapJsValue(externalArgs[i]) ); +// } +// caller(handler, args, k); +// }); +// }); +// } + + var anEffectType = types.makeEffectType(String(name), + superType, + fieldCnt, + impl, +// handlerIndices_js, + jsGuard, + makeCaller(aState)); + aState.v = getMakeStructTypeReturns(anEffectType); + }); + + +EXPORTS['effect-type?'] = new PrimProc('effect-type?', 1, false, false, types.isEffectType); +EXPORTS['effect?'] = new PrimProc('effect?', 1, false, false, types.isEffect); + +//EXPORTS['make-effect:do-nothing'] = new PrimProc('make-effect:do-nothing', 0, false, false, types.EffectDoNothing.constructor); +//EXPORTS['effect:do-nothing?'] = new PrimProc('effect:do-nothing?', 1, false, false, types.EffectDoNothing.predicate); + + +EXPORTS['make-render-effect-type'] = + makeOptionPrimitive( + 'make-render-effect-type', + 4, + [false], + true, + function(userArgs, aState, name, superType, fieldCnt, impl, guard) { + check(name, isSymbol, 'make-render-effect-type', 'string', 1, userArgs); + check(superType, function(x) { return x === false || types.isEffectType(x) }, + 'make-render-effect-type', 'effect type or #f', 2, userArgs); + check(fieldCnt, isNatural, 'make-render-effect-type', 'exact non-negative integer', 3, userArgs); + check(impl, isFunction, 'make-render-effect-type', 'procedure', 4, userArgs); + check(guard, function(x) { return x === false || isFunction(x); }, 'make-render-effect-type', 'procedure or #f', 6, userArgs); + // Check the number of arguments on the guard + var numberOfGuardArgs = fieldCnt + 1 + (superType ? superType.numberOfArgs : 0); + if ( guard && !procArityContains(numberOfGuardArgs)(guard) ) { + raise(types.incompleteExn( + types.exnFailContract, + helpers.format( + 'make-effect-type: guard procedure does not accept ~a arguments ' + + '(one more than the number constructor arguments): ~s', + [numberOfGuardArgs, guard]), + [])); + } + var jsGuard = (guard ? schemeProcToJs(aState, guard) : false); + + var aRenderEffectType = types.makeRenderEffectType(String(name), + superType, + fieldCnt, + impl, + jsGuard); + aState.v = getMakeStructTypeReturns(aRenderEffectType); + }); + + +EXPORTS['render-effect-type?'] = new PrimProc('render-effect-type?', 1, false, false, types.isRenderEffectType); +EXPORTS['render-effect?'] = new PrimProc('render-effect?', 1, false, false, types.isRenderEffect); + + +EXPORTS['world-with-effects'] = + new PrimProc('world-with-effects', + 2, + false, false, + function(effects, w) { + check(effects, isCompoundEffect, 'world-with-effects', 'compound effect', 1, arguments); + + return jsworld.Jsworld.with_multiple_effects(w, helpers.flattenSchemeListToArray(effects)); + }); + + + +EXPORTS['make-render-effect'] = new PrimProc('make-render-effect', 2, false, false, types.makeRenderEffect); + +EXPORTS['render-effect?'] = new PrimProc('render-effect?', 1, false, false, types.isRenderEffect); + +EXPORTS['render-effect-dom-node'] = + new PrimProc('render-effect-dom-node', + 1, + false, false, + function(effect) { + check(effect, types.isRenderEffect, 'render-effect-dom-node', 'render-effect', 1); + return types.renderEffectDomNode(effect); + }); + +EXPORTS['render-effect-effects'] = + new PrimProc('render-effect-effects', + 1, + false, false, + function(effect) { + check(effect, types.isRenderEffect, 'render-effect-effects', 'render-effect', 1); + return types.renderEffectEffects(effect); + }); + + + + + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////// + +// Helper Functions + + + + + + + + +var checkList = function(x, functionName, position, args) { + if ( !isList(x) ) { + helpers.throwCheckError([functionName, + 'list', + helpers.ordinalize(position), + x], + position, + args); + } +} + + +var length = function(lst) { + checkList(lst, 'length', 1, [lst]); + var ret = 0; + for (; !isEmpty(lst); lst = lst.rest()) { + ret = ret+1; + } + return ret; +} + + + + + + + + + + + + + + + + + +var getMakeStructTypeReturns = function(aStructType) { + var name = aStructType.name; + return new types.ValuesWrapper( + [aStructType, + (new types.StructConstructorProc(name, + 'make-'+name, + aStructType.numberOfArgs, + false, + false, + aStructType.constructor)), + (new types.StructPredicateProc(name, name+'?', 1, false, false, aStructType.predicate)), + (new types.StructAccessorProc(name, + name+'-ref', + 2, + false, + false, + function(x, i) { + check(x, aStructType.predicate, name+'-ref', 'struct:'+name, 1, arguments); + check(i, isNatural, name+'-ref', 'non-negative exact integer', 2, arguments); + + var numFields = aStructType.numberOfFields; + if ( jsnums.greaterThanOrEqual(i, numFields) ) { + var msg = (name+'-ref: slot index for not in ' + + '[0, ' + (numFields-1) + ']: ' + i); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + return aStructType.accessor(x, jsnums.toFixnum(i)); + })), + (new types.StructMutatorProc(name, + name+'-set!', + 3, + false, + false, + function(x, i, v) { + check(x, aStructType.predicate, name+'-set!', 'struct:'+name, 1, arguments); + check(i, isNatural, name+'-set!', 'non-negative exact integer', 2, arguments); + + var numFields = aStructType.numberOfFields; + if ( jsnums.greaterThanOrEqual(i, numFields) ) { + var msg = (name+'-set!: slot index for not in ' + + '[0, ' + (numFields-1) + ']: ' + i); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + aStructType.mutator(x, jsnums.toFixnum(i), v) + })) ]); +}; + + + + +////////////////////////////////////////////////////////////////////// + + +var isNumber = jsnums.isSchemeNumber; +var isReal = jsnums.isReal; +var isRational = jsnums.isRational; +var isComplex = isNumber; +var isInteger = jsnums.isInteger; + +var isNatural = function(x) { + return jsnums.isExact(x) && isInteger(x) && jsnums.greaterThanOrEqual(x, 0); +}; + +var isNonNegativeReal = function(x) { + return isReal(x) && jsnums.greaterThanOrEqual(x, 0); +}; + +var isSymbol = types.isSymbol; +var isChar = types.isChar; +var isString = types.isString; +var isPair = types.isPair; +var isEmpty = function(x) { return x === types.EMPTY; }; +var isList = helpers.isList; +var isListOf = helpers.isListOf; + +var isVector = types.isVector; +var isBox = types.isBox; +var isHash = types.isHash; +var isByteString = types.isByteString; + +var isByte = function(x) { + return (isNatural(x) && + jsnums.lessThanOrEqual(x, 255)); +} + +var isBoolean = function(x) { + return (x === true || x === false); +} + +var isFunction = types.isFunction; + +var isEqual = function(x, y) { + return types.isEqual(x, y, new types.UnionFind()); +} + +var isEq = function(x, y) { + return x === y; +} + +var isEqv = function(x, y) { + if (isNumber(x) && isNumber(y)) { + return jsnums.eqv(x, y); + } + else if (isChar(x) && isChar(y)) { + return x.val === y.val; + } + return x === y; +} + + + + + + +var isStyle = function(x) { + return ((isString(x) || isSymbol(x)) && + (String(x).toLowerCase() == "solid" || + String(x).toLowerCase() == "outline")); +}; + + +var isAssocList = function(x) { + return isPair(x) && isPair(x.rest()) && isEmpty(x.rest().rest()); +}; + + +var isCompoundEffect = function(x) { + return ( types.isEffect(x) || isListOf(x, isCompoundEffect) ); +}; + +var isJsValue = types.isJsValue; +var isJsFunction = function(x) { + return isJsValue(x) && typeof(x.unbox()) == 'function'; +}; + + + +var arrayEach = function(arr, f) { + for (var i = 0; i < arr.length; i++) { + f.call(null, arr[i], i); + } +} + +//var throwCheckError = helpers.throwCheckError; +var check = helpers.check; + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + + + + + +/* + + + +PRIMITIVES['js-p'] = + makeOptionPrimitive('js-p', + 0, + [types.EMPTY], + false, + function(userArgs, attribList) { + checkListOf(attribList, function(x) { return isList(x) && length(x) == 2; }, + 'js-p', 'list of (list of X Y)', 1, userArgs); + + var attribs = assocListToHash(attribList); + var node = helpers.wrapJsValue( jsworld.Jsworld.p(attribs) ); + + node.toWrittenString = function(cache) { return "(js-p)"; }; + node.toDisplayedString = node.toWrittenString; + // node.toDomNode = function(cache) { return node; }; + return node; + }); + + +PRIMITIVES['js-div'] = + makeOptionPrimitive('js-div', + 0, + [types.EMPTY], + false, + function(userArgs, attribList) { + checkListOf(attribList, isAssocList, 'js-div', '(listof X Y)', 1, userArgs); + + var attribs = assocListToHash(attribList); + var node = helpers.wrapJsValue( jsworld.Jsworld.div(attribs) ); + + node.toWrittenString = function(cache) { return "(js-div)"; }; + node.toDisplayedString = node.toWrittenString; + // node.toDomNode = function(cache) { return node; }; + return node; + }); + + +var jsButtonBang = function(funName, worldUpdateF, effectF, attribList) { + var attribs = assocListToHash(attribList); + var node = helpers.wrapJsValue( jsworld.Jsworld.buttonBang(worldUpdateF, effectF, attribs) ); + + node.toWrittenString = function(cache) { return '(' + funName + ' ...)'; }; + node.toDisplayedString = node.toWrittenString; +// node.toDomNode = function(cache) { return node; }; + return node; +}; +PRIMITIVES['js-button'] = + makeOptionPrimitive('js-button', + 1, + [types.EMPTY], + false, + function(userArgs, updateWorldF, attribList) { + check(updateWorldF, isFunction, 'js-button', 'procedure', 1, userArgs); + checkListOf(attribList, isAssocList, 'js-button', '(listof X Y)', 2, userArgs); + + var noneF = new types.PrimProc('', 1, false, false, function(w) { return types.EMPTY; }); + return jsButtonBang('js-button', updateWorldF, noneF, attribList); + }); + +PRIMITIVES['js-button!'] = + makeOptionPrimitive('js-button!', + 2, + [types.EMPTY], + false, + function(userArgs, updateWorldF, effectF, attribList) { + check(worldUpdateF, isFunction, funName, 'procedure', 1, userArgs); + check(effectF, isFunction, funName, 'procedure', 2, userArgs); + checkListOf(attribList, isAssocList, funName, '(listof X Y)', 3, userArgs); + + return jsButtonBang('js-button!', updateWorldF, effectF, attribList); + }); + + +PRIMITIVES['js-input'] = + makeOptionPrimitive('js-input', + 2, + [types.EMPTY], + false, + function(userArgs, type, updateF, attribList) { + check(type, isString, 'js-input', 'string', 1, userArgs); + check(updateF, isFunction, 'js-input', 'procedure', 2, userArgs); + checkListOf(attribList, isAssocList, 'js-input', '(listof X Y)', 3, userArgs); + + var attribs = assocListToHash(attribList); + var node = helpers.wrapJsValue( jsworld.Jsworld.input(type.toString(), updateF, attribs) ); + + node.toWrittenString = function(cache) { return "(js-input ...)"; } + node.toDisplayedString = node.toWrittenString; + // node.toDomNode = function(cache) { return node; } + return node; + }); + + +PRIMITIVES['js-img'] = + makeOptionPrimitive('js-img', + 1, + [types.EMPTY], + false, + function(userArgs, src, attribList) { + check(src, isString, "js-img", "string", 1, userArgs); + checkListOf(attribList, isAssocList, 'js-img', '(listof X Y)', 2, userArgs); + + var attribs = assocListToHash(attribList); + var node = helpers.wrapJsValue( jsworld.Jsworld.img(src.toString(), attribs) ); + + node.toWrittenString = function(cache) { return "(js-img ...)"; } + node.toDisplayedString = node.toWrittenString; + // node.toDomNode = function(cache) { return node; } + return node; + }); + + +PRIMITIVES['js-text'] = + new PrimProc('js-text', + 1, + false, false, + function(s) { + check(s, isString, 'js-text', 'string', 1); + + var node = helpers.wrapJsValue( jsworld.Jsworld.text(s.toString(), []) ); + node.toWrittenString = function(cache) { return "(js-text ...)"; } + node.toDisplayedString = node.toWrittenString; +// node.toDomNode = function(cache) { return node; } + return node; + }); + + +PRIMITIVES['js-select'] = + makeOptionPrimitive('js-select', + 2, + [types.EMPTY], + false, + function(userArgs, optionList, updateF, attribList) { + checkListOf(optionList, isString, 'js-select', 'listof string', 1, userArgs); + check(updateF, isFunction, 'js-select', 'procedure', 2, userArgs); + checkListOf(attribList, isAssocList, 'js-select', '(listof X Y)', 3, userArgs); + + var attribs = assocListToHash(attribList); + var options = helpers.deepListToArray(optionList); + for (var i = 0; i < options.length; i++) { + options[i] = options[i].toString(); + } + var node = helpers.wrapJsValue( jsworld.Jsworld.select(options, updateF, attribs) ); + + node.toWrittenString = function(cache) { return '(js-select ...)'; }; + node.toDisplayedString = node.toWrittenString; + // node.toDomNode = function(cache) { return node; }; + return node; + }); + + + +PRIMITIVES['js-big-bang'] = + new PrimProc('js-big-bang', + 1, + true, true, + function(aState, initW, configs) { + arrayEach(configs, + function(x, i) { + check(x, function(y) { return (types.isWorldConfig(y) || + jsworld.Jsworld.isBuiltInConfig(y)); }, + 'js-big-bang', 'world configuration', i+2); + }); + + return PAUSE(function(caller, onSuccess, onFail) { + var bigBangController = {}; + var onBreak = function() { + bigBangController.breaker(aState); + } + aState.addBreakRequestedListener(onBreak); + aState.onSuccess = function(v) { + aState.removeBreakRequestedListener(onBreak); + onSuccess(v); + }; + jsworld.Jsworld.bigBang(initW, +// aState.getToplevelNodeHook()(), + configs, + aState, + caller, + bigBangController); +// caller, +// function(v) { +// aState.removeBreakRequestedListener(onBreak); +// onSuccess(v); +// }, +// onFail, +// bigBangController); + }); + }); + + +////////////////////////////////////////////////////////////////////// + + + var emptyPage = function(attribList) { + checkListOf(attribList, isAssocList, 'empty-page', '(listof X Y)', 1); + + var attribs = assocListToHash(attribList); + var node = jsworld.MobyJsworld.emptyPage(attribs); + +// node.toWrittenString = function(cache) { return "(js-div)"; }; +// node.toDisplayedString = node.toWrittenString; +// node.toDomNode = function(cache) { return node; }; +// return helpers.wrapJsValue(node); + return node; + }; + + PRIMITIVES['empty-page'] = + new CasePrimitive('empty-page', + [new PrimProc('empty-page', 0, false, false, + function() { return emptyPage(types.EMPTY); }), + new PrimProc('empty-page', 1, false, false, emptyPage)]); + + + PRIMITIVES['place-on-page'] = + new PrimProc('empty-page', + 4, + false, false, + function(elt, left, top, page) { + // FIXME: add type checking + return jsworld.MobyJsworld.placeOnPage( + elt, left, top, page); + }); + + + + + +////////////////////////////////////////////////////////////////////// + + + + + +PRIMITIVES['make-world-config'] = + makeOptionPrimitive('make-world-config', + 2, + [false, false], + false, + function(userArgs, startup, shutdown, pause, restart) { + check(startup, procArityContains(1), 'make-world-config', 'procedure', 1, userArgs); + check(shutdown, procArityContains(1), 'make-world-config', 'procedure (arity 1)', 2, userArgs); + check(pause, function(x) { return (x === false || procArityContains(1)(x)); }, + 'make-world-config', 'procedure (arity 1) or #f', 3, userArgs); + check(restart, function(x) { return (x === false || procArityContains(2)(x)); }, + 'make-world-config', 'procedure (arity 2) or #f', 4, userArgs); + + return types.worldConfig(startup, shutdown, pause, restart); + }); + +PRIMITIVES['bb-info'] = types.BigBangInfo; +PRIMITIVES['make-bb-info'] = new PrimProc('make-bb-info', 2, false, false, types.makeBigBangInfo); +PRIMITIVES['bb-info?'] = new PrimProc('bb-info?', 1, false, false, types.isBigBangInfo); + +PRIMITIVES['bb-info-change-world'] = + new PrimProc('bb-info-change-world', + 1, + false, false, + function(bbInfo) { + check(bbInfo, types.isBigBangInfo, 'bb-info-change-world', 'bb-info', 1); + return types.bbInfoChangeWorld(bbInfo); + }); + +PRIMITIVES['bb-info-toplevel-node'] = + new PrimProc('bb-info-toplevel-node', + 1, + false, false, + function(bbInfo) { + check(bbInfo, types.isBigBangInfo, 'bb-info-toplevel-node', 'bb-info', 1); + return types.bbInfoToplevelNode(bbInfo); + }); + + +PRIMITIVES['make-effect-type'] = + makeOptionPrimitive( + 'make-effect-type', + 4, + [false], + true, + function(userArgs, aState, name, superType, fieldCnt, impl, guard) { + check(name, isSymbol, 'make-effect-type', 'string', 1, userArgs); + check(superType, function(x) { return x === false || types.isEffectType(x) }, + 'make-effect-type', 'effect type or #f', 2, userArgs); + check(fieldCnt, isNatural, 'make-effect-type', 'exact non-negative integer', 3, userArgs); + check(impl, isFunction, 'make-effect-type', 'procedure', 4, userArgs); + check(guard, function(x) { return x === false || isFunction(x); }, 'make-effect-type', 'procedure or #f', 6, userArgs); + + var numberOfGuardArgs = fieldCnt + 1 + (superType ? superType.numberOfArgs : 0); + var anEffectType = types.makeEffectType(name.toString(), + superType, + fieldCnt, + impl, + checkAndGetGuard('make-effect-type', + guard, + numberOfGuardArgs)); + aState.v = getMakeStructTypeReturns(anEffectType); + }); + + +PRIMITIVES['effect-type?'] = new PrimProc('effect-type?', 1, false, false, types.isEffectType); +PRIMITIVES['effect?'] = new PrimProc('effect?', 1, false, false, types.isEffect); + +//PRIMITIVES['make-effect:do-nothing'] = new PrimProc('make-effect:do-nothing', 0, false, false, types.EffectDoNothing.constructor); +//PRIMITIVES['effect:do-nothing?'] = new PrimProc('effect:do-nothing?', 1, false, false, types.EffectDoNothing.predicate); + + +PRIMITIVES['make-render-effect-type'] = + makeOptionPrimitive( + 'make-render-effect-type', + 4, + [false], + true, + function(userArgs, aState, name, superType, fieldCnt, impl, guard) { + check(name, isSymbol, 'make-render-effect-type', 'string', 1, userArgs); + check(superType, function(x) { return x === false || types.isEffectType(x) }, + 'make-render-effect-type', 'effect type or #f', 2, userArgs); + check(fieldCnt, isNatural, 'make-render-effect-type', 'exact non-negative integer', 3, userArgs); + check(impl, isFunction, 'make-render-effect-type', 'procedure', 4, userArgs); + check(guard, function(x) { return x === false || isFunction(x); }, 'make-render-effect-type', 'procedure or #f', 6, userArgs); + + var numberOfGuardArgs = fieldCnt + 1 + (superType ? superType.numberOfArgs : 0); + var aRenderEffectType = + types.makeRenderEffectType(name.toString(), + superType, + fieldCnt, + impl, + checkAndGetGuard('make-render-effect-type', + guard, + numberOfGuardArgs)); + aState.v = getMakeStructTypeReturns(aRenderEffectType); + }); + + +PRIMITIVES['render-effect-type?'] = new PrimProc('render-effect-type?', 1, false, false, types.isRenderEffectType); +PRIMITIVES['render-effect?'] = new PrimProc('render-effect?', 1, false, false, types.isRenderEffect); + + +PRIMITIVES['world-with-effects'] = + new PrimProc('world-with-effects', + 2, + false, false, + function(effects, w) { + check(effects, isCompoundEffect, 'world-with-effects', 'compound effect', 1, arguments); + + return jsworld.Jsworld.worldWithEffects(helpers.flattenSchemeListToArray(effects), w); + }); + + + +PRIMITIVES['make-render-effect'] = new PrimProc('make-render-effect', 2, false, false, types.makeRenderEffect); + +PRIMITIVES['render-effect?'] = new PrimProc('render-effect?', 1, false, false, types.isRenderEffect); + +PRIMITIVES['render-effect-dom-node'] = + new PrimProc('render-effect-dom-node', + 1, + false, false, + function(effect) { + check(effect, types.isRenderEffect, 'render-effect-dom-node', 'render-effect', 1); + return types.renderEffectDomNode(effect); + }); + +PRIMITIVES['render-effect-effects'] = + new PrimProc('render-effect-effects', + 1, + false, false, + function(effect) { + check(effect, types.isRenderEffect, 'render-effect-effects', 'render-effect', 1); + return types.renderEffectEffects(effect); + }); + + + + + + + + +PRIMITIVES['stop-when'] = + new PrimProc('stop-when', 1, false, false, + function(test) { + check(test, isFunction, 'stop-when', 'procedure', 1); + return jsworld.Jsworld.stopWhenConfig(test); + }); +//PRIMITIVES['stop-when!'] = new PrimProc('stop-when!', 2, false, false, +// onEventBang('stop-when!', 'stopWhen')); + + +PRIMITIVES['to-draw'] = + new PrimProc('to-draw', + 1, + false, false, + function(f) { + check(f, isFunction, 'to-draw', 'procedure', 1); + return jsworld.Jsworld.onDrawSceneConfig(f); + + }); + + +PRIMITIVES['to-draw-page'] = + new CasePrimitive('to-draw-page', + [new PrimProc('to-draw-page', + 1, + false, false, + function(domHandler) { + check(domHandler, isFunction, 'to-draw-page', 'procedure', 1); + return jsworld.Jsworld.onDrawPageConfig(domHandler); + }), + new PrimProc('to-draw-page', + 2, + false, false, + function(domHandler, styleHandler) { + check(domHandler, isFunction, 'to-draw-page', 'procedure', 1, arguments); + check(styleHandler, isFunction, 'to-draw-page', 'procedure', 2, arguments); + return jsworld.Jsworld.onDrawPageConfig(domHandler, styleHandler); + }) ]); + + + +*/ \ No newline at end of file diff --git a/world/scratch/jsworld/jsworld.rkt b/world/scratch/jsworld/jsworld.rkt new file mode 100644 index 0000000..e706821 --- /dev/null +++ b/world/scratch/jsworld/jsworld.rkt @@ -0,0 +1,50 @@ +#lang s-exp "../lang/js-impl/js-impl.rkt" + +;; Loaded so we have access to image library stuff, as well as the world kernel +(require "../world/kernel.rkt" + "../image/image.rkt") + + +(require-js "private/jsworld/jsworld.js" + "private/jsworld.js" + "jsworld.js") + + +(provide big-bang + to-draw + to-draw-page + + key=? + on-tick on-tick! + on-key on-key! + stop-when stop-when! + + initial-effect + + js-p + js-div + js-button + js-button! + js-input + js-img + js-text + js-select + + + empty-page + place-on-page + + make-world-config + make-effect-type + effect-type? + effect? + + #;make-render-effect-type + #;render-effect-type? + + world-with-effects + + #;make-render-effect + #;render-effect? + #;render-effect-dom-node + #;render-effect-effects) diff --git a/world/scratch/jsworld/private/jsworld.js b/world/scratch/jsworld/private/jsworld.js new file mode 100644 index 0000000..17a0cc8 --- /dev/null +++ b/world/scratch/jsworld/private/jsworld.js @@ -0,0 +1,777 @@ +// Depends on world.js, world-config.js + +(function() { + + var world = {}; + world.Kernel = STATE.invokedModules["mzscheme-vm/world/kernel"].lookup("kernel"); + + + + var Jsworld = jsworld.MobyJsworld = {}; + + // The real low-level jsworld module: + var _js = jsworld.Jsworld; + + + var caller; + var setCaller = function(c) { + caller = function(op, args, k) { + c(op, args, k, handleError); + }; + }; + var unsetCaller = function() { + caller = function(op, args, k) { + throw new Error('caller not defined!'); + }; + }; + unsetCaller(); + + // The restarted and things to set it + // Note that we never want to restart the same computation + // more than once, so we throw an error if someone tries to do that + var restarter; + var setRestarter = function(r) { + var hasRestarted = false; + restarter = function(v) { + if (hasRestarted) { + throw new Error('Cannot restart twice!'); + } + hasRestarted = true; + r(v); + }; + }; + var unsetRestarter = function() { + restarter = function() { + throw new Error('restarter not defined!'); + }; + }; + unsetRestarter(); + + + var errorReporter = function(e) { + // default: do nothing. + }; + + + + var terminator; + var setTerminator = function(t) { + terminator = t; + }; + var unsetTerminator = function() { + terminator = function() { + throw new Error('terminator not defined!'); + }; + }; + unsetTerminator(); + + + + // mutateStringsInDeepArray: array -> array + // walks and in-place mutates Scheme strings to primitive strings. + var mutateStringsInDeepArray = function(thing) { + var i, length; + if (typeof(thing) === 'object' && + thing.constructor === Array) { + length = thing.length; + for (i = 0; i < length; i++) { + thing[i] = mutateStringsInDeepArray(thing[i]); + } + } else if (types.isString(thing)) { + return thing.toString(); + } + return thing; + }; + + + + + var userConfigs = []; + + var startUserConfigs = function(k) { + helpers.forEachK(userConfigs, + function(aConfig, k2) { + caller(aConfig.startup, aConfig.startupArgs, + function(res) { + aConfig.isRunning = true; + aConfig.shutdownArg = res; + k2() + }); + }, + handleError, + k); + } + + var shutdownUserConfigs = function(k) { +// console.log('shutting down user configs'); + var theConfigs = userConfigs; + userConfigs = [] + helpers.forEachK(theConfigs, + function(aConfig, k2) { +// console.log(' shutting down a config'); + if (aConfig.isRunning) { + aConfig.isRunning = false; + caller(aConfig.shutdown, [aConfig.shutdownArg], k2); + } else { + k2(); + } + }, + handleError, + k); + } + + var expandHandler = function(handler) { + return types.jsValue('function', function() { + var wrappedStimulusArgs = []; + for (var i = 0; i < arguments.length; i++) { + wrappedStimulusArgs.push( helpers.wrapJsValue(arguments[i]) ); + } + + Jsworld.updateWorld( + function(w, k) { + var args = [w].concat(wrappedStimulusArgs); + caller(handler, args, k); + }, + function() {}); + }); + }; + + +// var unwrapWorldEffects = function(w) { +// if ( _js.has_effects(w) ) { +// var unwrappedEffects = +// helpers.map(function(e) { +// if ( types.isEffect(e) ) { +// return types.makeJsworldEffect(function(k) { +// caller(types.effectThunk(e), [], k); +// }); +// } +// else { +// return e; +// } +// }, +// w.getEffects()); +// var returnVal = _js.with_multiple_effects(w.getWorld(), unwrappedEffects); +// return returnVal; +// } +// else { +// return w; +// } +// }; + + + var deepUnwrapJsValues = function(x, k) { + if ( types.isJsValue(x) ) { + k(x.unbox()); + } + else if ( types.isRenderEffect(x) ) { + x.callImplementation(caller, function(y) { deepUnwrapJsValues(y, k); }); + } +// var effects = helpers.schemeListToArray( types.renderEffectEffects(x) ).reverse(); +// types.setRenderEffectEffects(x, types.EMPTY); +// +// helpers.forEachK(effects, +// function(ef, k2) { caller(ef, [], k2); }, +// handleError, +// function() { deepUnwrapJsValues(types.renderEffectDomNode(x), k); }); +// } + else if ( types.isPair(x) ) { + deepUnwrapJsValues(x.first(), function(first) { + deepUnwrapJsValues(x.rest(), function(rest) { + k( types.cons(first, rest) ); + }); + }); + } + else { + k(x); + } + }; + + + + + + + + + // isHandler: X -> boolean + // Right now, a handler is a function that consumes and produces + // configs. We should tighten up the type check eventually. + var isHandler = function(x) { + return typeof(x) == 'function'; + } + + + + + ////////////////////////////////////////////////////////////////////// + //From this point forward, we define wrappers to integrate jsworld + //with Moby. + + + // getBigBangWindow: -> window + var getBigBangWindow = function() { + if (window.document.getElementById("jsworld-div") !== undefined) { + return window; + } else { + var newDiv = window.document.createElement("div"); + newDiv.id = 'jsworld-div'; + window.document.appendChild(newDiv); + return window; + } + } + + + // types are + // sexp: (cons node (listof sexp)) + // css-style: (node (listof (list string string))) + + // Exports: + + + + + var isPair = types.isPair; + var isEmpty = function(x) { return x === types.EMPTY; }; + var isList = function(x) { return (isPair(x) || isEmpty(x)); }; + + + + // The default printWorldHook will write the written content of the node. + // We probably want to invoke the pretty printer here instead! + Jsworld.printWorldHook = function(world, node) { + var newNode; + if(node.lastChild == null) { + newNode = types.toDomNode(world); + node.appendChild(newNode); + helpers.maybeCallAfterAttach(newNode); + } else { + newNode = types.toDomNode(world); + node.replaceChild(newNode, node.lastChild); + helpers.maybeCallAfterAttach(newNode); + } + }; + + + + // Figure out the target of an event. + // http://www.quirksmode.org/js/events_properties.html#target + var findEventTarget = function(e) { + var targ; + if (e.target) + targ = e.target; + else if (e.srcElement) + targ = e.srcElement; + if (targ.nodeType == 3) // defeat Safari bug + targ = targ.parentNode; + return targ; + } + + // isNode: any -> boolean + // Returns true if the thing has a nodeType. + var isNode = function(thing) { + return thing && typeof(thing.nodeType) != 'undefined'; + } + + + + // checkWellFormedDomTree: X X (or number undefined) -> void + // Check to see if the tree is well formed. If it isn't, + // we need to raise a meaningful error so the user can repair + // the structure. + // + // Invariants: + // The dom tree must be a pair. + // The first element must be a node. + // Each of the rest of the elements must be dom trees. + // If the first element is a text node, it must NOT have children. + var checkWellFormedDomTree = function(x, top, index) { + var fail = function(formatStr, formatArgs) { + throw types.schemeError( + types.incompleteExn(types.exnFailContract, + helpers.format(formatStr, formatArgs), + [])); + } + + if (_js.isPage(x)) { + return; + } + + if (types.isPair(x)) { + var firstElt = x.first(); + var restElts = x.rest(); + + if (! isNode(firstElt)) { + fail("on-draw: expected a dom-element, but received ~s instead, the first element within ~s", [firstElt, top]); + } + + if (firstElt.nodeType == Node.TEXT_NODE && !restElts.isEmpty() ) { + fail("on-draw: the text node ~s must not have children. It has ~s", [firstElt, restElts]); + } + + var i = 2; + while( !restElts.isEmpty() ) { + checkWellFormedDomTree(restElts.first(), x, i); + restElts = restElts.rest(); + i++; + } + } else { + var formatStr = "on-draw: expected a dom-s-expression, but received ~s instead"; + var formatArgs = [x]; + if (index != undefined) { + formatStr += ", the ~a element within ~s"; + formatArgs.push( helpers.ordinalize(index) ); + formatArgs.push(top); + } + formatStr += "."; + + fail(formatStr, formatArgs); + } + }; + + + // Compatibility for attaching events to nodes. + var attachEvent = function(node, eventName, fn) { + if (node.addEventListener) { + // Mozilla + node.addEventListener(eventName, fn, false); + } else { + // IE + node.attachEvent('on' + eventName, fn, false); + } + return function() { + detachEvent(node, eventName, fn); + } + }; + + var detachEvent = function(node, eventName, fn) { + if (node.addEventListener) { + // Mozilla + node.removeEventListener(eventName, fn, false); + } else { + // IE + node.detachEvent('on' + eventName, fn, false); + } + } + + + var preventDefault = function(event) { + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + } + + var stopPropagation = function(event) { + if (event.stopPropagation) { + event.stopPropagation(); + } else { + event.cancelBubble = true; + } + } + + + // bigBang: world dom (listof (list string string)) (arrayof handler) -> world + Jsworld.bigBang = function(initWorld, toplevelNode, handlers, theCaller, theRestarter, onFail) { + // shutdownListeners: arrayof (-> void) + // We maintain a list of thunks that need to be called as soon as we come out of + // bigBang, to do cleanup. + var shutdownListeners = []; + + var onTermination = function(w) { + for (var i = 0; i < shutdownListeners.length; i++) { + try { + shutdownListeners[i](); + } catch (e) { } + } + shutdownUserConfigs(function() { + unsetCaller(); + theRestarter(w); + }); + } + + + //console.log('in high level big-bang'); + errorReporter = onFail; + + setCaller(theCaller); + setRestarter(theRestarter); + setTerminator(function(w) { + detachEvent(toplevelNode, 'click', absorber); + shutdownUserConfigs(function() { + unsetCaller(); + unsetTerminator(); + restarter(w); + }); + }); + + var attribs = types.EMPTY; + + // Ensure that the toplevelNode can be focused by mouse or keyboard + toplevelNode.tabIndex = 0; + + // Absorb all click events so they don't bubble up. + var absorber = function(e) { + preventDefault(e); + stopPropagation(e); + return false; + } + + attachEvent(toplevelNode, 'click', absorber); + shutdownListeners.push(function() { detachEvent(toplevelNode, 'click', absorber)}); + + + + var config = new world.Kernel.config.WorldConfig(); + for(var i = 0; i < handlers.length; i++) { + if (isList(handlers[i])) { + attribs = handlers[i]; + } + else if (isHandler(handlers[i])) { + config = handlers[i](config); + } + else if ( types.isWorldConfig(handlers[i]) ) { + handlers[i].startupArgs = helpers.map(expandHandler, handlers[i].startupArgs); + userConfigs.push(handlers[i]); + } + } + config = config.updateAll({'changeWorld': Jsworld.updateWorld, + 'shutdownWorld': Jsworld.shutdownWorld}); + var stimuli = new world.Kernel.stimuli.StimuliHandler(config, caller, restarter); + + var wrappedHandlers = []; + var wrappedRedraw; + var wrappedRedrawCss; + + + if (config.lookup('onDraw')) { + wrappedRedraw = function(w, k) { + try { + caller(config.lookup('onDraw'), [w], + function(newDomTree) { + deepUnwrapJsValues(newDomTree, function(unwrappedTree) { + checkWellFormedDomTree(unwrappedTree, unwrappedTree, undefined); + var result = [toplevelNode, + helpers.deepListToArray(unwrappedTree)]; + k(result); + }); + }); + } catch (e) { + handleError(e); +// throw e; + } + } + + if (config.lookup('onDrawCss')) { + wrappedRedrawCss = function(w, k) { + try { + caller(config.lookup('onDrawCss'), [w], + function(res) { + var result = helpers.deepListToArray(res); + result = mutateStringsInDeepArray(result); + // plt.Kernel.setLastLoc(undefined); + k(result); + }); + } catch (e) { + handleError(e); + // throw e; + } + } + } + else { + wrappedRedrawCss = function(w, k) { k([]); }; + } + wrappedHandlers.push(_js.on_draw(wrappedRedraw, wrappedRedrawCss)); + } else if (config.lookup('onRedraw')) { + var reusableCanvas = undefined; + var reusableCanvasNode = undefined; + + wrappedRedraw = function(w, k) { + try { + //console.log('in onRedraw handler'); + caller(config.lookup('onRedraw'), [w], + function(aScene) { + // Performance hack: if we're using onRedraw, we know + // we've got a scene, so we optimize away the repeated + // construction of a canvas object. + if ( world.Kernel.isImage(aScene) ) { + var width = aScene.getWidth(); + var height = aScene.getHeight(); + + if (! reusableCanvas) { + reusableCanvas = world.Kernel.makeCanvas(width, height); + // Note: the canvas object may itself manage objects, + // as in the case of an excanvas. In that case, we must make + // sure jsworld doesn't try to disrupt its contents! + reusableCanvas.jsworldOpaque = true; + reusableCanvasNode = _js.node_to_tree(reusableCanvas); + } + + reusableCanvas.width = width; + reusableCanvas.height = height; + var ctx = reusableCanvas.getContext("2d"); + aScene.render(ctx, 0, 0); + + k([toplevelNode, reusableCanvasNode]); + } else { + k([toplevelNode, _js.node_to_tree(types.toDomNode(aScene))]); + } + }); + } catch (e) { + handleError(e); +// throw e; + } + } + + wrappedRedrawCss = function(w, k) { + //console.log('in RedrawCss handler'); + k([[reusableCanvas, + ["width", reusableCanvas.width + "px"], + ["height", reusableCanvas.height + "px"]]]); + } + wrappedHandlers.push(_js.on_draw(wrappedRedraw, wrappedRedrawCss)); + } else { + wrappedHandlers.push(_js.on_world_change + (function(w, k) { + Jsworld.printWorldHook(w, toplevelNode); + k(); + })); + } + + if (config.lookup('tickDelay')) { + var wrappedTick = function(w, k) { + caller(config.lookup('onTick'), + [w], + k); + } + var wrappedDelay = jsnums.toFixnum( config.lookup('tickDelay') ); + wrappedHandlers.push(_js.on_tick(wrappedDelay, wrappedTick)); + } + + if (config.lookup('stopWhen')) { + wrappedHandlers.push(_js.stop_when( + function(w, k) { + caller(config.lookup('stopWhen'), [w], + function(res) { k(res); }); + })); + } + + + if (config.lookup('onKey')) { + var wrappedKey = function(w, e, k) { + caller(config.lookup('onKey'), [w, helpers.getKeyCodeName(e)], k); + } + wrappedHandlers.push(_js.on_key(wrappedKey)); + toplevelNode.focus(); + } + + + if (config.lookup('initialEffect')) { + var updaters = + world.Kernel.applyEffect(config.lookup('initialEffect')); + for (var i = 0 ; i < updaters.length; i++) { + if (config.lookup('stopWhen') && + config.lookup('stopWhen')([initWorld])) { + break; + } else { + initWorld = updaters[i](initWorld); + } + } + } + + + _js.big_bang(toplevelNode, + initWorld, + wrappedHandlers, + helpers.assocListToHash(attribs), + terminator); + + startUserConfigs(function() {}); + + return { + breaker: function() { + handleError(types.schemeError( + types.incompleteExn(types.exnBreak, 'user break', []))); + } + }; + + } + + + + var handleError = function(e) { +// helpers.reportError(e); + // When something bad happens, shut down + // the world computation. +// helpers.reportError("Shutting down jsworld computations"); +// world.Kernel.stimuli.onShutdown(); + world.Kernel.stimuli.massShutdown(); + shutdownUserConfigs(function() { + errorReporter(e); +// console.log('Got an error, the error was:'); +// console.log(e); + if (typeof(console) !== 'undefined' && console.log) { + if (e.stack) { + console.log(e.stack); + } + else { + console.log(e); + } + } + if ( types.isSchemeError(e) ) { + terminator(e); + } + else if ( types.isInternalError(e) ) { + terminator(e); + } + else if (typeof(e) == 'string') { + terminator( types.schemeError(types.incompleteExn(types.exnFail, e, [])) ); + } + else if (e instanceof Error) { + terminator( types.schemeError(types.incompleteExn(types.exnFail, e.message, [])) ); + } + else { + terminator( types.schemeError(e) ); + } + }); + } + + + + // updateWorld: CPS( CPS(world -> world) -> void ) + Jsworld.updateWorld = function(updater, k) { + var wrappedUpdater = function(w, k2) { + try { + updater(w, k2); + } catch (e) { + if (typeof(console) !== 'undefined' && console.log && e.stack) { + console.log(e.stack); + } + handleError(e); +// k2(w); + } + } + + _js.change_world(wrappedUpdater, k); + } + + + + // shutdownWorld: -> void + // Shut down all world computations. + Jsworld.shutdownWorld = function() { + _js.shutdown(); + }; + + +// var getAttribs = function(args) { +// if (args.length == 0) { +// return [] +// } +// if (args.length == 1) { +// return helpers.assocListToHash(args[0]); +// } else { +// throw new Error("getAttribs recevied unexpected value for args: " +// + args); +// } +// } + + + Jsworld.p = _js.p; + + Jsworld.div = _js.div; + + Jsworld.buttonBang = function(updateWorldF, effectF, attribs) { + var wrappedF = function(w, evt, k) { + try { +// FIXME: Get effects back online! +// caller(effectF, [world], +// function(effect) { + caller(updateWorldF, [w], + function(newWorld) { +// world.Kernel.applyEffect(effect); + k(newWorld); + }); +// }); + } catch (e) { + if (typeof(console) !== 'undefined' && console.log && e.stack) { + console.log(e.stack); + } + handleError(e); +// k(w); + } + } + return _js.button(wrappedF, attribs); + }; + + + Jsworld.input = function(type, updateF, attribs) { + var wrappedUpdater = function(w, evt, k) { + caller(updateF, [w, evt], k); + } + return _js.input(type, wrappedUpdater, attribs); + }; + + + Jsworld.get_dash_input_dash_value = function(node) { +// plt.Kernel.check(node, +// function(x) { return (plt.Kernel.isString(node) || +// node.nodeType == +// Node.ELEMENT_NODE) }, +// "get-input-value", +// "dom-node", +// 1); + if (types.isString(node)) { + return (document.getElementById(node).value || ""); + } else { + return (node.value || ""); + } + + }; + + + + // Images. + Jsworld.img = _js.img; + + // text: string -> node + Jsworld.text = _js.text; + + Jsworld.select = function(options, updateF, attribs) { + var wrappedUpdater = function(w, e, k) { +// console.log(e); + caller(updateF, [w, e.target.value], k); + } + return _js.select(attribs, options, wrappedUpdater); + }; + + + + + ////////////////////////////////////////////////////////////////////// + Jsworld.emptyPage = _js.emptyPage; + + Jsworld.placeOnPage = function(elt, left, top, page) { + deepUnwrapJsValues(elt, function(newElt) { + elt = types.toDomNode(newElt);}); + return _js.placeOnPage(elt, left, top, page); + }; + + + // fixme: add support for textarea, h1, canvas + + +// // raw_node: scheme-value assoc -> node +// Jsworld.rawNode = function(x, args) { +// var attribs = getAttribs(args); +// var node = _js.raw_node(types.toDomNode(x), attribs); +// node.toWrittenString = function(cache) { return "(js-raw-node ...)"; } +// node.toDisplayedString = node.toWrittenString; +// node.toDomNode = function(cache) { return node; } +// return node; +// }; + + + +})(); diff --git a/world/scratch/jsworld/private/jsworld/jsworld.js b/world/scratch/jsworld/private/jsworld/jsworld.js new file mode 100644 index 0000000..f4d315d --- /dev/null +++ b/world/scratch/jsworld/private/jsworld/jsworld.js @@ -0,0 +1,1488 @@ +var jsworld = {}; + +// Stuff here is copy-and-pasted from Chris's JSWorld. We +// namespace-protect it, and add the Javascript <-> Moby wrapper +// functions here. + +(function() { + + /* Type signature notation + * CPS(a b ... -> c) is used to denote + * a b ... (c -> void) -> void + */ + + jsworld.Jsworld = {}; + var Jsworld = jsworld.Jsworld; + + + var currentFocusedNode = false; + + var doNothing = function() {}; + + + + // + // WORLD STUFFS + // + + function InitialWorld() {} + + var world = new InitialWorld(); + var worldListeners = []; + var eventDetachers = []; + var runningBigBangs = []; + + var changingWorld = false; + + + + // Close all world computations. + Jsworld.shutdown = function() { + while(runningBigBangs.length > 0) { + var currentRecord = runningBigBangs.pop(); + if (currentRecord) { currentRecord.pause(); } + } + clear_running_state(); + } + + + + function add_world_listener(listener) { + worldListeners.push(listener); + } + + + function remove_world_listener(listener) { + var index = worldListeners.indexOf(listener); + if (index != -1) { + worldListeners.splice(index, 1); + } + } + + function clear_running_state() { + world = new InitialWorld(); + worldListeners = []; + + for (var i = 0; i < eventDetachers.length; i++) { + eventDetachers[i](); + } + eventDetachers = []; + changingWorld = false; + } + + + // If we're in the middle of a change_world, delay. + var DELAY_BEFORE_RETRY = 10; + + + // change_world: CPS( CPS(world -> world) -> void ) + // Adjust the world, and notify all listeners. + var change_world = function(updater, k) { + + // Check to see if we're in the middle of changing + // the world already. If so, put on the queue + // and exit quickly. + if (changingWorld) { + setTimeout( + function() { + change_world(updater, k)}, + DELAY_BEFORE_RETRY); + return; + } + + + changingWorld = true; + var originalWorld = world; + + var changeWorldHelp = function() { + if (world instanceof WrappedWorldWithEffects) { + var effects = world.getEffects(); + helpers.forEachK(effects, + function(anEffect, k2) { + anEffect.invokeEffect(change_world, k2); + }, + function (e) { + changingWorld = false; + throw e; + }, + function() { + world = world.getWorld(); + changeWorldHelp2(); + }); + } else { + changeWorldHelp2(); + } + }; + + var changeWorldHelp2 = function() { + helpers.forEachK(worldListeners, + function(listener, k2) { + listener(world, originalWorld, k2); + }, + function(e) { + changingWorld = false; + world = originalWorld; + throw e; }, + function() { + changingWorld = false; + k(); + }); + }; + + try { + updater(world, function(newWorld) { + world = newWorld; + changeWorldHelp(); + }); + } catch(e) { + changingWorld = false; + world = originalWorld; + + if (typeof(console) !== 'undefined' && console.log && e.stack) { + console.log(e.stack); + } + throw e; + } + } + Jsworld.change_world = change_world; + + + + + // + // STUFF THAT SHOULD REALLY BE IN ECMASCRIPT + // + Number.prototype.NaN0=function(){return isNaN(this)?0:this;} + function getPosition(e){ + var left = 0; + var top = 0; + while (e.offsetParent){ + left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0); + top += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0); + e = e.offsetParent; + } + left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0); + top += e.offsetTop + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0); + return {x:left, y:top}; + } + Jsworld.getPosition = getPosition; + + + var gensym_counter = 0; + function gensym(){ return gensym_counter++;} + Jsworld.gensym = gensym; + + + function map(a1, f) { + var b = new Array(a1.length); + for (var i = 0; i < a1.length; i++) { + b[i] = f(a1[i]); + } + return b; + } + Jsworld.map = map; + + + + function concat_map(a, f) { + var b = []; + for (var i = 0; i < a.length; i++) { + b = b.concat(f(a[i])); + } + return b; + } + + + function mapi(a, f) { + var b = new Array(a.length); + for (var i = 0; i < a.length; i++) { + b[i] = f(a[i], i); + } + return b; + } + Jsworld.mapi = mapi; + + + function fold(a, x, f) { + for (var i = 0; i < a.length; i++) { + x = f(a[i], x); + } + return x; + } + Jsworld.fold = fold; + + + function augment(o, a) { + var oo = {}; + for (var e in o) + oo[e] = o[e]; + for (var e in a) + oo[e] = a[e]; + return oo; + } + Jsworld.augment = augment; + + + function assoc_cons(o, k, v) { + var oo = {}; + for (var e in o) + oo[e] = o[e]; + oo[k] = v; + return oo; + } + Jsworld.assoc_cons = assoc_cons; + + + function cons(value, array) { + return [value].concat(array); + } + Jsworld.cons = cons; + + + function append(array1, array2){ + return array1.concat(array2); + } + Jsworld.append = append; + + function array_join(array1, array2){ + var joined = []; + for (var i = 0; i < array1.length; i++) + joined.push([array1[i], array2[i]]); + return joined; + } + Jsworld.array_join = array_join; + + + function removeq(a, value) { + for (var i = 0; i < a.length; i++) + if (a[i] === value){ + return a.slice(0, i).concat(a.slice(i+1)); + } + return a; + } + Jsworld.removeq = removeq; + + function removef(a, value) { + for (var i = 0; i < a.length; i++) + if ( f(a[i]) ){ + return a.slice(0, i).concat(a.slice(i+1)); + } + return a; + } + Jsworld.removef = removef; + + + function filter(a, f) { + var b = []; + for (var i = 0; i < a.length; i++) { + if ( f(a[i]) ) { + b.push(a[i]); + } + } + return b; + } + Jsworld.filter = filter; + + + function without(obj, attrib) { + var o = {}; + for (var a in obj) + if (a != attrib) + o[a] = obj[a]; + return o; + } + Jsworld.without = without; + + + function memberq(a, x) { + for (var i = 0; i < a.length; i++) + if (a[i] === x) return true; + return false; + } + Jsworld.memberq = memberq; + + + function member(a, x) { + for (var i = 0; i < a.length; i++) + if (a[i] == x) return true; + return false; + } + Jsworld.member = member; + + + + function head(a){ + return a[0]; + } + Jsworld.head = head; + + + function tail(a){ + return a.slice(1, a.length); + } + Jsworld.tail = tail; + + // + // DOM UPDATING STUFFS + // + + // tree(N): { node: N, children: [tree(N)] } + // relation(N): { relation: 'parent', parent: N, child: N } | { relation: 'neighbor', left: N, right: N } + // relations(N): [relation(N)] + // nodes(N): [N] + // css(N): [css_node(N)] + // css_node(N): { node: N, attribs: attribs } | { className: string, attribs: attribs } + // attrib: { attrib: string, values: [string] } + // attribs: [attrib] + + // treeable(nodes(N), relations(N)) = bool + /*function treeable(nodes, relations) { + // for all neighbor relations between x and y + for (var i = 0; i < relations.length; i++) + if (relations[i].relation == 'neighbor') { + var x = relations[i].left, y = relations[i].right; + + // there does not exist a neighbor relation between x and z!=y or z!=x and y + for (var j = 0; j < relations.length; j++) + if (relations[j].relation === 'neighbor') + if (relations[j].left === x && relations[j].right !== y || + relations[j].left !== x && relations[j].right === y) + return false; + } + + // for all parent relations between x and y + for (var i = 0; i < relations.length; i++) + if (relations[i].relation == 'parent') { + var x = relations[i].parent, y = relations[i].child; + + // there does not exist a parent relation between z!=x and y + for (var j = 0; j < relations.length; j++) + if (relations[j].relation == 'parent') + if (relations[j].parent !== x && relations[j].child === y) + return false; + } + + // for all neighbor relations between x and y + for (var i = 0; i < relations.length; i++) + if (relations[i].relation == 'neighbor') { + var x = relations[i].left, y = relations[i].right; + + // all parent relations between z and x or y share the same z + for (var j = 0; j < relations.length; j++) + if (relations[j].relation == 'parent') + for (var k = 0; k < relations.length; k++) + if (relations[k].relation == 'parent') + if (relations[j].child === x && relations[k].child === y && + relations[j].parent !== relations[k].parent) + return false; + } + + return true; + }*/ + + + // node_to_tree: dom -> dom-tree + // Given a native dom node, produces the appropriate tree. + function node_to_tree(domNode) { + var result = [domNode]; + for (var c = domNode.firstChild; c != null; c = c.nextSibling) { + result.push(node_to_tree(c)); + } + return result; + } + Jsworld.node_to_tree = node_to_tree; + + + + // nodes(tree(N)) = nodes(N) + function nodes(tree) { + var ret; + + if (tree.node.jsworldOpaque == true) { + return [tree.node]; + } + + ret = [tree.node]; + for (var i = 0; i < tree.children.length; i++) + ret = ret.concat(nodes(tree.children[i])); + + return ret; + } + + + // relations(tree(N)) = relations(N) + function relations(tree) { + var ret = []; + + for (var i = 0; i < tree.children.length; i++) + ret.push({ relation: 'parent', + parent: tree.node, + child: tree.children[i].node }); + + for (var i = 0; i < tree.children.length - 1; i++) + ret.push({ relation: 'neighbor', + left: tree.children[i].node, + right: tree.children[i + 1].node }); + + if (! tree.node.jsworldOpaque) { + for (var i = 0; i < tree.children.length; i++) { + ret = ret.concat(relations(tree.children[i])); + } + } + + return ret; + } + + + + var removeAllChildren = function(n) { + while (n.firstChild) { + n.removeChild(n.firstChild); + } + } + + + // Preorder traversal. + var preorder = function(node, f) { + f(node, function() { + var child = node.firstChild; + var nextSibling; + while (child) { + var nextSibling = child.nextSibling; + preorder(child, f); + child = nextSibling; + } + }); + }; + + + // update_dom(nodes(Node), relations(Node)) = void + function update_dom(toplevelNode, nodes, relations) { + + // TODO: rewrite this to move stuff all in one go... possible? necessary? + + // move all children to their proper parents + for (var i = 0; i < relations.length; i++) { + if (relations[i].relation == 'parent') { + var parent = relations[i].parent, child = relations[i].child; + if (child.parentNode !== parent) { + parent.appendChild(child); + } + } + } + + // arrange siblings in proper order + // truly terrible... BUBBLE SORT + var unsorted = true; + while (unsorted) { + unsorted = false; + for (var i = 0; i < relations.length; i++) { + if (relations[i].relation == 'neighbor') { + var left = relations[i].left, right = relations[i].right; + + if (! nodeEq(left.nextSibling, right)) { + left.parentNode.insertBefore(left, right) + unsorted = true; + } + } + } + } + + // Finally, remove nodes that shouldn't be attached anymore. + var nodesPlus = nodes.concat([toplevelNode]); + preorder(toplevelNode, function(aNode, continueTraversalDown) { + if (aNode.jsworldOpaque) { + if (! isMemq(aNode, nodesPlus)) { + aNode.parentNode.removeChild(aNode); + } + } else { + if (! isMemq(aNode, nodesPlus)) { + aNode.parentNode.removeChild(aNode); + } else { + continueTraversalDown(); + } + } + }); + + refresh_node_values(nodes); + } + + + // isMemq: X (arrayof X) -> boolean + // Produces true if any of the elements of L are nodeEq to x. + var isMemq = function(x, L) { + var i; + for (i = 0 ; i < L.length; i++) { + if (nodeEq(x, L[i])) { + return true; + } + } + return false; + }; + + + // nodeEq: node node -> boolean + // Returns true if the two nodes should be the same. + var nodeEq = function(node1, node2) { + return (node1 && node2 && node1 === node2); + } + + + + // camelCase: string -> string + function camelCase(name) { + return name.replace(/\-(.)/g, function(m, l){return l.toUpperCase()}); + } + + + function set_css_attribs(node, attribs) { + for (var j = 0; j < attribs.length; j++){ + node.style[camelCase(attribs[j].attrib)] = attribs[j].values.join(" "); + } + } + + + // isMatchingCssSelector: node css -> boolean + // Returns true if the CSS selector matches. + function isMatchingCssSelector(node, css) { + if (css.id.match(/^\./)) { + // Check to see if we match the class + return ('className' in node && member(node['className'].split(/\s+/), + css.id.substring(1))); + } else { + return ('id' in node && node.id == css.id); + } + } + + + function update_css(nodes, css) { + // clear CSS + for (var i = 0; i < nodes.length; i++) { + if ( !nodes[i].jsworldOpaque ) { + clearCss(nodes[i]); + } + } + + // set CSS + for (var i = 0; i < css.length; i++) + if ('id' in css[i]) { + for (var j = 0; j < nodes.length; j++) + if (isMatchingCssSelector(nodes[j], css[i])) { + set_css_attribs(nodes[j], css[i].attribs); + } + } + else set_css_attribs(css[i].node, css[i].attribs); + } + + + var clearCss = function(node) { + // FIXME: we should not be clearing the css +// if ('style' in node) +// node.style.cssText = ""; + } + + + + // If any node cares about the world, send it in. + function refresh_node_values(nodes) { + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].onWorldChange) { + nodes[i].onWorldChange(world); + } + } + } + + + + function do_redraw(world, oldWorld, toplevelNode, redraw_func, redraw_css_func, k) { + if (oldWorld instanceof InitialWorld) { + // Simple path + redraw_func(world, + function(drawn) { + var t = sexp2tree(drawn); + var ns = nodes(t); + // HACK: css before dom, due to excanvas hack. + redraw_css_func(world, + function(css) { + update_css(ns, sexp2css(css)); + update_dom(toplevelNode, ns, relations(t)); + k(); + }); + }); + } else { + maintainingSelection( + function(k2) { + // For legibility, here is the non-CPS version of the same function: + /* + var oldRedraw = redraw_func(oldWorld); + var newRedraw = redraw_func(world); + var oldRedrawCss = redraw_css_func(oldWorld); + var newRedrawCss = redraw_css_func(world); + var t = sexp2tree(newRedraw); + var ns = nodes(t); + + // Try to save the current selection and preserve it across + // dom updates. + + if(oldRedraw !== newRedraw) { + // Kludge: update the CSS styles first. + // This is a workaround an issue with excanvas: any style change + // clears the content of the canvas, so we do this first before + // attaching the dom element. + update_css(ns, sexp2css(newRedrawCss)); + update_dom(toplevelNode, ns, relations(t)); + } else { + if(oldRedrawCss !== newRedrawCss) { + update_css(ns, sexp2css(newRedrawCss)); + } + } + */ + + // We try to avoid updating the dom if the value + // hasn't changed. + redraw_func(oldWorld, + function(oldRedraw) { + redraw_func(world, + function(newRedraw) { + redraw_css_func(oldWorld, + function(oldRedrawCss) { + redraw_css_func(world, + function(newRedrawCss) { + var t = sexp2tree(newRedraw); + var ns = nodes(t); + + // Try to save the current selection and preserve it across + // dom updates. + + if(oldRedraw !== newRedraw) { + // Kludge: update the CSS styles first. + // This is a workaround an issue with excanvas: any style change + // clears the content of the canvas, so we do this first before + // attaching the dom element. + update_css(ns, sexp2css(newRedrawCss)); + update_dom(toplevelNode, ns, relations(t)); + } else { + if (oldRedrawCss !== newRedrawCss) { + update_css(ns, sexp2css(newRedrawCss)); + } + } + k2(); + }) + }) + }) + }); + }, k); + } + } + + + // maintainingSelection: (-> void) -> void + // Calls the thunk f while trying to maintain the current focused selection. + function maintainingSelection(f, k) { + var currentFocusedSelection; + if (hasCurrentFocusedSelection()) { + currentFocusedSelection = getCurrentFocusedSelection(); + f(function() { + currentFocusedSelection.restore(); + k(); + }); + } else { + f(function() { k(); }); + } + } + + + + function FocusedSelection() { + this.focused = currentFocusedNode; + this.selectionStart = currentFocusedNode.selectionStart; + this.selectionEnd = currentFocusedNode.selectionEnd; + } + + // Try to restore the focus. + FocusedSelection.prototype.restore = function() { + // FIXME: if we're scrolling through, what's visible + // isn't restored yet. + if (this.focused.parentNode) { + this.focused.selectionStart = this.selectionStart; + this.focused.selectionEnd = this.selectionEnd; + this.focused.focus(); + } else if (this.focused.id) { + var matching = document.getElementById(this.focused.id); + if (matching) { + matching.selectionStart = this.selectionStart; + matching.selectionEnd = this.selectionEnd; + matching.focus(); + } + } + }; + + function hasCurrentFocusedSelection() { + return currentFocusedNode != undefined; + } + + function getCurrentFocusedSelection() { + return new FocusedSelection(); + } + + + + ////////////////////////////////////////////////////////////////////// + + function BigBangRecord(top, world, handlerCreators, handlers, attribs) { + this.top = top; + this.world = world; + this.handlers = handlers; + this.handlerCreators = handlerCreators; + this.attribs = attribs; + } + + BigBangRecord.prototype.restart = function() { + big_bang(this.top, this.world, this.handlerCreators, this.attribs); + } + + BigBangRecord.prototype.pause = function() { + for(var i = 0 ; i < this.handlers.length; i++) { + if (this.handlers[i] instanceof StopWhenHandler) { + // Do nothing for now. + } else { + this.handlers[i].onUnregister(top); + } + } + }; + ////////////////////////////////////////////////////////////////////// + + // Notes: big_bang maintains a stack of activation records; it should be possible + // to call big_bang re-entrantly. + function big_bang(top, init_world, handlerCreators, attribs, k) { + // clear_running_state(); + + // Construct a fresh set of the handlers. + var handlers = map(handlerCreators, function(x) { return x();} ); + if (runningBigBangs.length > 0) { + runningBigBangs[runningBigBangs.length - 1].pause(); + } + + // Create an activation record for this big-bang. + var activationRecord = + new BigBangRecord(top, init_world, handlerCreators, handlers, attribs); + runningBigBangs.push(activationRecord); + function keepRecordUpToDate(w, oldW, k2) { + activationRecord.world = w; + k2(); + } + add_world_listener(keepRecordUpToDate); + + + + // Monitor for termination and register the other handlers. + var stopWhen = new StopWhenHandler(function(w, k2) { k2(false); }, + function(w, k2) { k2(w); }); + for(var i = 0 ; i < handlers.length; i++) { + if (handlers[i] instanceof StopWhenHandler) { + stopWhen = handlers[i]; + } else { + handlers[i].onRegister(top); + } + } + function watchForTermination(w, oldW, k2) { + stopWhen.test(w, + function(stop) { + if (stop) { + Jsworld.shutdown(); + k(w); + /* + stopWhen.receiver(world, + function() { + var currentRecord = runningBigBangs.pop(); + if (currentRecord) { currentRecord.pause(); } + if (runningBigBangs.length > 0) { + var restartingBigBang = runningBigBangs.pop(); + restartingBigBang.restart(); + } + k(); + }); + */ + } + else { k2(); } + }); + }; + add_world_listener(watchForTermination); + + + // Finally, begin the big-bang. + copy_attribs(top, attribs); + change_world(function(w, k2) { k2(init_world); }, doNothing); + + + } + Jsworld.big_bang = big_bang; + + + + + + // on_tick: number CPS(world -> world) -> handler + function on_tick(delay, tick) { + return function() { + var scheduleTick, ticker; + + + (new Date()).valueOf() + + scheduleTick = function(t) { + ticker.watchId = setTimeout( + function() { + ticker.watchId = undefined; + var startTime = (new Date()).valueOf(); + change_world(tick, + function() { + var endTime = (new Date()).valueOf(); + scheduleTick(Math.max(delay - (endTime - startTime), + 0)); + }); + }, + t); + }; + + ticker = { + watchId: -1, + onRegister: function (top) { + scheduleTick(delay); + }, + + onUnregister: function (top) { + if (ticker.watchId) + clearTimeout(ticker.watchId); + } + }; + return ticker; + }; + } + Jsworld.on_tick = on_tick; + + + function on_key(press) { + return function() { + var wrappedPress = function(e) { + preventDefault(e); + stopPropagation(e); + change_world(function(w, k) { press(w, e, k); }, doNothing); + }; + return { + onRegister: function(top) { attachEvent(top, 'keydown', wrappedPress); }, + onUnregister: function(top) { detachEvent(top, 'keydown', wrappedPress); } + }; + } + } + Jsworld.on_key = on_key; + + + + // on_draw: CPS(world -> (sexpof node)) CPS(world -> (sexpof css-style)) -> handler + function on_draw(redraw, redraw_css) { + var wrappedRedraw = function(w, k) { + redraw(w, function(newDomTree) { + checkDomSexp(newDomTree, newDomTree); + k(newDomTree); + }); + } + + return function() { + var drawer = { + _top: null, + _listener: function(w, oldW, k2) { + do_redraw(w, oldW, drawer._top, wrappedRedraw, redraw_css, k2); + }, + onRegister: function (top) { + drawer._top = top; + add_world_listener(drawer._listener); + }, + + onUnregister: function (top) { + remove_world_listener(drawer._listener); + } + }; + return drawer; + }; + } + Jsworld.on_draw = on_draw; + + + + function StopWhenHandler(test, receiver) { + this.test = test; + this.receiver = receiver; + } + // stop_when: CPS(world -> boolean) CPS(world -> boolean) -> handler + function stop_when(test, receiver) { + return function() { + if (receiver == undefined) { + receiver = function(w, k) { k(w); }; + } + return new StopWhenHandler(test, receiver); + }; + } + Jsworld.stop_when = stop_when; + + + + function on_world_change(f) { + var listener = function(world, oldW, k) { f(world, k); }; + return function() { + return { + onRegister: function (top) { + add_world_listener(listener); }, + onUnregister: function (top) { + remove_world_listener(listener)} + }; + }; + } + Jsworld.on_world_change = on_world_change; + + + + + + // Compatibility for attaching events to nodes. + function attachEvent(node, eventName, fn) { + if (node.addEventListener) { + // Mozilla + node.addEventListener(eventName, fn, false); + } else { + // IE + node.attachEvent('on' + eventName, fn, false); + } + } + + var detachEvent = function(node, eventName, fn) { + if (node.addEventListener) { + // Mozilla + node.removeEventListener(eventName, fn, false); + } else { + // IE + node.detachEvent('on' + eventName, fn, false); + } + } + + // + // DOM CREATION STUFFS + // + + // add_ev: node string CPS(world event -> world) -> void + // Attaches a world-updating handler when the world is changed. + function add_ev(node, event, f) { + var eventHandler = function(e) { change_world(function(w, k) { f(w, e, k); }, + doNothing); }; + attachEvent(node, event, eventHandler); + eventDetachers.push(function() { detachEvent(node, event, eventHandler); }); + } + + // add_ev_after: node string CPS(world event -> world) -> void + // Attaches a world-updating handler when the world is changed, but only + // after the fired event has finished. + function add_ev_after(node, event, f) { + var eventHandler = function(e) { + setTimeout(function() { change_world(function(w, k) { f(w, e, k); }, + doNothing); }, + 0); + }; + + attachEvent(node, event, eventHandler); + eventDetachers.push(function() { detachEvent(node, event, eventHandler); }); + } + + + function addFocusTracking(node) { + attachEvent(node, "focus", function(e) { + currentFocusedNode = node; }); + attachEvent(node, "blur", function(e) { + currentFocusedNode = undefined; + }); + return node; + } + + + + + + // + // WORLD STUFFS + // + + + function sexp2tree(sexp) { + if (isPage(sexp)) { + return sexp2tree(node_to_tree(sexp.toDomNode())); + } else { + if(sexp.length == undefined) return { node: sexp, children: [] }; + else return { node: sexp[0], children: map(sexp.slice(1), sexp2tree) }; + } + } + + function sexp2attrib(sexp) { + return { attrib: sexp[0], values: sexp.slice(1) }; + } + + function sexp2css_node(sexp) { + var attribs = map(sexp.slice(1), sexp2attrib); + if (typeof sexp[0] == 'string'){ + return [{ id: sexp[0], attribs: attribs }]; + } else if ('length' in sexp[0]){ + return map(sexp[0], function (id) { return { id: id, attribs: attribs } }); + } else { + return [{ node: sexp[0], attribs: attribs }]; + } + } + + function sexp2css(sexp) { + return concat_map(sexp, sexp2css_node); + } + + + + function isTextNode(n) { + return (n.nodeType == Node.TEXT_NODE); + } + + + function isElementNode(n) { + return (n.nodeType == Node.ELEMENT_NODE); + } + + + var throwDomError = function(thing, topThing) { + throw new JsworldDomError( + helpers.format( + "Expected a non-empty array, received ~s within ~s", + [thing, topThing]), + thing); + }; + + // checkDomSexp: X X -> boolean + // Checks to see if thing is a DOM-sexp. If not, + // throws an object that explains why not. + function checkDomSexp(thing, topThing) { + if (isPage(thing)) { + return; + } + + if (! thing instanceof Array) { + throwDomError(thing, topThing); + } + if (thing.length == 0) { + throwDomError(thing, topThing); + } + + // Check that the first element is a Text or an element. + if (isTextNode(thing[0])) { + if (thing.length > 1) { + throw new JsworldDomError(helpers.format("Text node ~s can not have children", + [thing]), + thing); + } + } else if (isElementNode(thing[0])) { + for (var i = 1; i < thing.length; i++) { + checkDomSexp(thing[i], thing); + } + } else { + throw new JsworldDomError( + helpers.format( + "expected a Text or an Element, received ~s within ~s", + [thing, topThing]), + thing[0]); + } + } + + function JsworldDomError(msg, elt) { + this.msg = msg; + this.elt = elt; + } + JsworldDomError.prototype.toString = function() { + return "on-draw: " + this.msg; + } + + + + + + // + // DOM CREATION STUFFS + // + + + function copy_attribs(node, attribs) { + if (attribs) + for (a in attribs) { + if (attribs.hasOwnProperty(a)) { + if (typeof attribs[a] == 'function') + add_ev(node, a, attribs[a]); + else{ + node[a] = attribs[a];//eval("node."+a+"='"+attribs[a]+"'"); + } + } + } + return node; + } + + + // + // NODE TYPES + // + + function p(attribs) { + return addFocusTracking(copy_attribs(document.createElement('p'), attribs)); + } + Jsworld.p = p; + + function div(attribs) { + return addFocusTracking(copy_attribs(document.createElement('div'), attribs)); + } + Jsworld.div = div; + + // Used To Be: (world event -> world) (hashof X Y) -> domElement + // Now: CPS(world event -> world) (hashof X Y) -> domElement + function button(f, attribs) { + var n = document.createElement('button'); + n.onclick = function(e) {return false;}; + add_ev(n, 'click', f); + return addFocusTracking(copy_attribs(n, attribs)); + } + Jsworld.button = button; + + + + + var preventDefault = function(event) { + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + } + + var stopPropagation = function(event) { + if (event.stopPropagation) { + event.stopPropagation(); + } else { + event.cancelBubble = true; + } + } + + + var stopClickPropagation = function(node) { + attachEvent(node, "click", + function(e) { + stopPropagation(e); + }); + return node; + } + + + // input: string CPS(world -> world) + function input(aType, updateF, attribs) { + aType = aType.toLowerCase(); + var dispatchTable = { text : text_input, + password: text_input, + checkbox: checkbox_input + //button: button_input, + //radio: radio_input + }; + + if (dispatchTable[aType]) { + return (dispatchTable[aType])(aType, updateF, attribs); + } + else { + throw new Error("js-input: does not currently support type " + aType); + } + } + Jsworld.input = input; + + + + + var text_input = function(type, updateF, attribs) { + var n = document.createElement('input'); + n.type = type; + + var lastVal = n.value; + var onEvent = function() { + if (! n.parentNode) { return; } + setTimeout( + function() { + if (lastVal != n.value) { + lastVal = n.value; + change_world(function (w, k) { + updateF(w, n.value, k); + }, doNothing); + } + }, + 0); + } + + +// attachEvent(n, "keypress", onEvent); +// eventDetachers.push(function() { +// detachEvent(n, "keypress", onEvent); }); + + attachEvent(n, "keydown", onEvent); + eventDetachers.push(function() { + detachEvent(n, "keydown", onEvent); }); + + attachEvent(n, "change", onEvent); + eventDetachers.push(function() { + detachEvent(n, "change", onEvent); }); + +// function onKey(w, e, k) { +// updateF(w, n.value, k); +// } +// // This established the widget->world direction +// add_ev_after(n, 'keypress', onKey); + + // Every second, do a manual polling of the object, just in case. +// var delay = 1000; +// var intervalId = setInterval(function() { +// if (! n.parentNode) { +// clearInterval(intervalId); +// return; +// } +// if (lastVal != n.value) { +// lastVal = n.value; +// change_world(function (w, k) { +// updateF(w, n.value, k); +// }, doNothing); +// } +// }, +// delay); + return stopClickPropagation( + addFocusTracking(copy_attribs(n, attribs))); + }; + + + var checkbox_input = function(type, updateF, attribs) { + var n = document.createElement('input'); + n.type = type; + var onCheck = function(w, e, k) { + updateF(w, n.checked, k); + }; + // This established the widget->world direction + add_ev_after(n, 'change', onCheck); + + attachEvent(n, 'click', function(e) { + stopPropagation(e); + }); + + return copy_attribs(n, attribs); + }; + + + var button_input = function(type, updateF, attribs) { + var n = document.createElement('button'); + add_ev(n, 'click', function(w, e, k) { updateF(w, n.value, k); }); + return addFocusTracking(copy_attribs(n, attribs)); + }; + + + + + + function text(s, attribs) { + var result = document.createElement("div"); + result.appendChild(document.createTextNode(String(s))); + result.jsworldOpaque = true; + return result; + } + Jsworld.text = text; + + function select(attribs, opts, f){ + var n = document.createElement('select'); + for(var i = 0; i < opts.length; i++) { + n.add(option({value: opts[i]}), null); + } + n.jsworldOpaque = true; + add_ev(n, 'change', f); + var result = addFocusTracking(copy_attribs(n, attribs)); + return result; + } + Jsworld.select = select; + + function option(attribs){ + var node = document.createElement("option"); + node.text = attribs.value; + node.value = attribs.value; + return node; + } + + + + function textarea(attribs){ + return addFocusTracking(copy_attribs(document.createElement('textarea'), attribs)); + } + Jsworld.textarea = textarea; + + function h1(attribs){ + return addFocusTracking(copy_attribs(document.createElement('h1'), attribs)); + } + Jsworld.h1 = h1; + + function canvas(attribs){ + return addFocusTracking(copy_attribs(document.createElement('canvas'), attribs)); + } + Jsworld.canvas = canvas; + + + function img(src, attribs) { + var n = document.createElement('img'); + n.src = src; + return addFocusTracking(copy_attribs(n, attribs)); + } + Jsworld.img = img; + + + + function raw_node(node, attribs) { + return addFocusTracking(copy_attribs(node, attribs)); + } + Jsworld.raw_node = raw_node; + + + + + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + // Effects + + // An effect is an object with an invokeEffect() method. + + var WrappedWorldWithEffects = function(w, effects) { + if (w instanceof WrappedWorldWithEffects) { + this.w = w.w; + this.e = w.e.concat(effects); + } else { + this.w = w; + this.e = effects; + } + }; + + WrappedWorldWithEffects.prototype.getWorld = function() { + return this.w; + }; + + WrappedWorldWithEffects.prototype.getEffects = function() { + return this.e; + }; + + + ////////////////////////////////////////////////////////////////////// + + Jsworld.with_effect = function(w, e) { + return new WrappedWorldWithEffects(w, [e]); + }; + + Jsworld.with_multiple_effects = function(w, effects) { + return new WrappedWorldWithEffects(w, effects); + }; + + Jsworld.has_effects = function(w) { + return w instanceof WrappedWorldWithEffects; + }; + + + + + ////////////////////////////////////////////////////////////////////// + // Example effect: raise an alert. + Jsworld.alert_effect = function(msg) { + return new AlertEffect(msg); + }; + + var AlertEffect = function(msg) { + this.msg = msg; + }; + + AlertEffect.prototype.invokeEffect = function(k) { + alert(this.msg); + k(); + }; + + + ////////////////////////////////////////////////////////////////////// + + + // Example effect: play a song, given its url + Jsworld.music_effect = function(musicUrl) { + return new MusicEffect(musicUrl); + }; + + var MusicEffect = function(musicUrl) { + this.musicUrl = musicUrl; + }; + + MusicEffect.prototype.invokeEffect = function(k) { + new Audio(url).play(); + k(); + }; + + + + + + ////////////////////////////////////////////////////////////////////// + // Pages + + + var Page = function(elts, attribs) { + if (typeof(elts) === 'undefined') { + elts = []; + } + this.elts = elts; + this.attribs = attribs; + }; + + Page.prototype.add = function(elt, positionLeft, positionTop) { + return new Page(this.elts.concat([{elt: elt, + positionTop: positionTop, + positionLeft: positionLeft}]), + this.attribs); + }; + + Page.prototype.toDomNode = function() { + var aDiv = div(); + for (var i = 0 ; i < this.elts.length; i++) { + var elt = this.elts[i].elt; + if (! elt.style) { + elt.style = ''; + } + + elt.style.position = 'absolute'; + elt.style.left = this.elts[i].positionLeft + "px"; + elt.style.top = this.elts[i].positionTop + "px"; + aDiv.appendChild(elt); + }; + copy_attribs(aDiv, this.attribs) + return aDiv; + }; + + + isPage = function(x) { + return x instanceof Page; + }; + + Jsworld.isPage = isPage; + + Jsworld.emptyPage = function(attribs) { + var result = new Page([], attribs); + return result; + }; + + Jsworld.placeOnPage = function(elt, positionLeft, positionTop, page) { + if (typeof(elt) === 'string') { + elt = text(elt); + } + return page.add(elt, positionLeft, positionTop); + }; + + + +})(); diff --git a/world/scratch/world/compiled/kernel_rkt.dep b/world/scratch/world/compiled/kernel_rkt.dep new file mode 100644 index 0000000..63c7550 --- /dev/null +++ b/world/scratch/world/compiled/kernel_rkt.dep @@ -0,0 +1 @@ +("5.1.1" ("20b08da4180281e3468edf47b1d9c436989a35c2" . "c5c6223485c4a0496ad4df78dd013eb9a4700ebb") #"/home/dyoo/work/js-vm/world/../lang/js-impl/js-impl.rkt" (collects #"s-exp" #"lang" #"reader.rkt")) diff --git a/world/scratch/world/compiled/kernel_rkt.zo b/world/scratch/world/compiled/kernel_rkt.zo new file mode 100644 index 0000000000000000000000000000000000000000..8e695be70c1b2304e56b5e45081aefbcc9f7dbbd GIT binary patch literal 56139 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 Hi*NrQvW`l` literal 0 HcmV?d00001 diff --git a/world/scratch/world/kernel.js b/world/scratch/world/kernel.js new file mode 100644 index 0000000..c0b51f9 --- /dev/null +++ b/world/scratch/world/kernel.js @@ -0,0 +1,1938 @@ +var world = {}; +world.Kernel = {}; + +EXPORTS['kernel'] = world.Kernel; + + + + + +var worldListeners = []; +var stopped; +var timerInterval = false; + + +// Inheritance from pg 168: Javascript, the Definitive Guide. +var heir = function(p) { + var f = function() {} + f.prototype = p; + return new f(); +} + + +// clone: object -> object +// Copies an object. The new object should respond like the old +// object, including to things like instanceof +var clone = function(obj) { + var C = function() {} + C.prototype = obj; + var c = new C(); + for (property in obj) { + if (obj.hasOwnProperty(property)) { + c[property] = obj[property]; + } + } + return c; +}; + + + + +var announceListeners = []; +world.Kernel.addAnnounceListener = function(listener) { + announceListeners.push(listener); +}; +world.Kernel.removeAnnounceListener = function(listener) { + var idx = announceListeners.indexOf(listener); + if (idx != -1) { + announceListeners.splice(idx, 1); + } +}; +world.Kernel.announce = function(eventName, vals) { + for (var i = 0; i < announceListeners.length; i++) { + try { + announceListeners[i](eventName, vals); + } catch (e) {} + } +}; + + + + + + + + + + +// changeWorld: world -> void +// Changes the current world to newWorld. +var changeWorld = function(newWorld) { + world = newWorld; + notifyWorldListeners(); +} + + +// updateWorld: (world -> world) -> void +// Public function: update the world, given the old state of the +// world. +world.Kernel.updateWorld = function(updater) { + var newWorld = updater(world); + changeWorld(newWorld); +} + + +world.Kernel.shutdownWorld = function() { + stopped = true; +}; + + +// notifyWorldListeners: -> void +// Tells all of the world listeners that the world has changed. +var notifyWorldListeners = function() { + var i; + for (i = 0; i < worldListeners.length; i++) { + worldListeners[i](world); + } +} + +// addWorldListener: (world -> void) -> void +// Adds a new world listener: whenever the world is changed, the aListener +// will be called with that new world. +var addWorldListener = function(aListener) { + worldListeners.push(aListener); +} + + +// getKeyCodeName: keyEvent -> String +// Given an event, try to get the name of the key. +var getKeyCodeName = function(e) { + var code = e.charCode || e.keyCode; + var keyname; + if (code == 37) { + keyname = "left"; + } else if (code == 38) { + keyname = "up"; + } else if (code == 39) { + keyname = "right"; + } else if (code == 40) { + keyname = "down"; + } else { + keyname = String.fromCharCode(code); + } + return keyname; +} + + +// resetWorld: -> void +// Resets all of the world global values. +var resetWorld = function() { + if (timerInterval) { + clearInterval(timerInterval); + timerInterval = false; + } + stopped = false; + worldListeners = []; +} + + +var getBigBangWindow = function(width, height) { + if (window.document.getElementById("canvas") != undefined) { + return window; + } + + var newWindow = window.open( + "big-bang.html", + "big-bang"); + //"toolbar=false,location=false,directories=false,status=false,menubar=false,width="+width+",height="+height); + if (newWindow == null) { + throw new Error("Error: Not allowed to create a new window."); } + + return newWindow; +} + + + +// scheduleTimerTick: -> void +// Repeatedly schedules an evaluation of the onTick until the program has stopped. +var scheduleTimerTick = function(window, config) { + timerInterval = window.setInterval( + function() { + if (stopped) { + window.clearTimeout(timerInterval); + timerInterval = false; + } + else { + world.Kernel.stimuli.onTick(); + } + }, + config.lookup('tickDelay')); +} + + + + +// Base class for all images. +var BaseImage = function(pinholeX, pinholeY) { + this.pinholeX = pinholeX; + this.pinholeY = pinholeY; +} +world.Kernel.BaseImage = BaseImage; + + +var isImage = function(thing) { + return (thing !== null && + thing !== undefined && + thing instanceof BaseImage); +} + + + +BaseImage.prototype.updatePinhole = function(x, y) { + var aCopy = clone(this); + aCopy.pinholeX = x; + aCopy.pinholeY = y; + return aCopy; +}; + + + +// render: context fixnum fixnum: -> void +// Render the image, where the upper-left corner of the image is drawn at +// (x, y). +// NOTE: the rendering should be oblivous to the pinhole. +BaseImage.prototype.render = function(ctx, x, y) { + throw new Error('BaseImage.render unimplemented!'); + // plt.Kernel.throwMobyError( + // false, + // "make-moby-error-type:generic-runtime-error", + // "Unimplemented method render"); +}; + + +// makeCanvas: number number -> canvas +// Constructs a canvas object of a particular width and height. +world.Kernel.makeCanvas = function(width, height) { + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + + canvas.style.width = canvas.width + "px"; + canvas.style.height = canvas.height + "px"; + + // KLUDGE: IE compatibility uses /js/excanvas.js, and dynamic + // elements must be marked this way. + if (window && typeof window.G_vmlCanvasManager != 'undefined') { + canvas = window.G_vmlCanvasManager.initElement(canvas); + } + return canvas; +}; + + + +var withIeHack = function(canvas, f) { + // canvas.style.display = 'none'; + // document.body.appendChild(canvas); + // try { + var result = f(canvas); + // } catch(e) { + // document.body.removeChild(canvas); + // canvas.style.display = ''; + // throw e; + // } + // document.body.removeChild(canvas); + // canvas.style.display = ''; + return result; +}; + + +BaseImage.prototype.toDomNode = function(cache) { + var that = this; + var width = that.getWidth(); + var height = that.getHeight(); + var canvas = world.Kernel.makeCanvas(width, height); + + // KLUDGE: on IE, the canvas rendering functions depend on a + // context where the canvas is attached to the DOM tree. + + // We initialize an afterAttach hook; the client's responsible + // for calling this after the dom node is attached to the + // document. + canvas.afterAttach = function() { + var ctx = canvas.getContext("2d"); + that.render(ctx, 0, 0); + }; + + return canvas; +}; + + + + +BaseImage.prototype.toWrittenString = function(cache) { return ""; } +BaseImage.prototype.toDisplayedString = function(cache) { return ""; } + +BaseImage.prototype.isEqual = function(other, aUnionFind) { + return (this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY); +}; + + + + +// isScene: any -> boolean +// Produces true when x is a scene. +var isScene = function(x) { + return ((x != undefined) && (x != null) && (x instanceof SceneImage)); +}; + +// SceneImage: primitive-number primitive-number (listof image) -> Scene +var SceneImage = function(width, height, children, withBorder) { + BaseImage.call(this, 0, 0); + this.width = width; + this.height = height; + this.children = children; // arrayof [image, number, number] + this.withBorder = withBorder; +} +SceneImage.prototype = heir(BaseImage.prototype); + + +// add: image primitive-number primitive-number -> Scene +SceneImage.prototype.add = function(anImage, x, y) { + return new SceneImage(this.width, + this.height, + this.children.concat([[anImage, + x - anImage.pinholeX, + y - anImage.pinholeY]]), + this.withBorder); +}; + +// render: 2d-context primitive-number primitive-number -> void +SceneImage.prototype.render = function(ctx, x, y) { + var i; + var childImage, childX, childY; + // Clear the scene. + ctx.clearRect(x, y, this.width, this.height); + // Then ask every object to render itself. + for(i = 0; i < this.children.length; i++) { + childImage = this.children[i][0]; + childX = this.children[i][1]; + childY = this.children[i][2]; + ctx.save(); + childImage.render(ctx, childX + x, childY + y); + ctx.restore(); + + + } + // Finally, draw the black border if withBorder is true + if (this.withBorder) { + ctx.strokeStyle = 'black'; + ctx.strokeRect(x, y, this.width, this.height); + } +}; + +SceneImage.prototype.getWidth = function() { + return this.width; +}; + +SceneImage.prototype.getHeight = function() { + return this.height; +}; + +SceneImage.prototype.isEqual = function(other, aUnionFind) { + if (!(other instanceof SceneImage)) { + return false; + } + + if (this.pinholeX != other.pinholeX || + this.pinholeY != other.pinholeY || + this.width != other.width || + this.height != other.height || + this.children.length != other.children.length) { + return false; + } + + for (var i = 0; i < this.children.length; i++) { + var rec1 = this.children[i]; + var rec2 = other.children[i]; + if (rec1[1] !== rec2[1] || + rec1[2] !== rec2[2] || + !types.isEqual(rec1[0], + rec2[0], + aUnionFind)) { + return false; + } + } + return true; +}; + + +////////////////////////////////////////////////////////////////////// + + +var FileImage = function(src, rawImage) { + BaseImage.call(this, 0, 0); + var self = this; + this.src = src; + this.isLoaded = false; + if (rawImage && rawImage.complete) { + this.img = rawImage; + this.isLoaded = true; + this.pinholeX = self.img.width / 2; + this.pinholeY = self.img.height / 2; + } else { + // fixme: we may want to do something blocking here for + // onload, since we don't know at this time what the file size + // should be, nor will drawImage do the right thing until the + // file is loaded. + this.img = new Image(); + this.img.onload = function() { + self.isLoaded = true; + self.pinholeX = self.img.width / 2; + self.pinholeY = self.img.height / 2; + }; + this.img.onerror = function(e) { + self.img.onerror = ""; + self.img.src = "http://www.wescheme.org/images/broken.png"; + } + this.img.src = src; + } +} +FileImage.prototype = heir(BaseImage.prototype); +// world.Kernel.FileImage = FileImage; + + +var imageCache = {}; +FileImage.makeInstance = function(path, rawImage) { + if (! (path in imageCache)) { + imageCache[path] = new FileImage(path, rawImage); + } + return imageCache[path]; +}; + +FileImage.installInstance = function(path, rawImage) { + imageCache[path] = new FileImage(path, rawImage); +}; + +FileImage.installBrokenImage = function(path) { + imageCache[path] = new TextImage("Unable to load " + path, 10, + colorDb.get("red")); +}; + + + +FileImage.prototype.render = function(ctx, x, y) { + ctx.drawImage(this.img, x, y); +}; + + +FileImage.prototype.getWidth = function() { + return this.img.width; +}; + + +FileImage.prototype.getHeight = function() { + return this.img.height; +}; + +// Override toDomNode: we don't need a full-fledged canvas here. +FileImage.prototype.toDomNode = function(cache) { + return this.img.cloneNode(true); +}; + +FileImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof FileImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.src == other.src); + // types.isEqual(this.img, other.img, aUnionFind)); +}; + + +////////////////////////////////////////////////////////////////////// + + +// OverlayImage: image image -> image +// Creates an image that overlays img1 on top of the +// other image. shiftX and shiftY are deltas off the first +// image's pinhole. +var OverlayImage = function(img1, img2, shiftX, shiftY) { + var deltaX = img1.pinholeX - img2.pinholeX + shiftX; + var deltaY = img1.pinholeY - img2.pinholeY + shiftY; + var left = Math.min(0, deltaX); + var top = Math.min(0, deltaY); + var right = Math.max(deltaX + img2.getWidth(), + img1.getWidth()); + var bottom = Math.max(deltaY + img2.getHeight(), + img1.getHeight()); + + BaseImage.call(this, + Math.floor((right-left) / 2), + Math.floor((bottom-top) / 2)); + this.img1 = img1; + this.img2 = img2; + this.width = right - left; + this.height = bottom - top; + + this.img1Dx = -left; + this.img1Dy = -top; + this.img2Dx = deltaX - left; + this.img2Dy = deltaY - top; +}; + +OverlayImage.prototype = heir(BaseImage.prototype); + + +OverlayImage.prototype.render = function(ctx, x, y) { + this.img2.render(ctx, x + this.img2Dx, y + this.img2Dy); + this.img1.render(ctx, x + this.img1Dx, y + this.img1Dy); +}; + + +OverlayImage.prototype.getWidth = function() { + return this.width; +}; + +OverlayImage.prototype.getHeight = function() { + return this.height; +}; + +OverlayImage.prototype.isEqual = function(other, aUnionFind) { + return ( other instanceof OverlayImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.img1Dx == other.img1Dx && + this.img1Dy == other.img1Dy && + this.img2Dx == other.img2Dx && + this.img2Dy == other.img2Dy && + types.isEqual(this.img1, other.img1, aUnionFind) && + types.isEqual(this.img2, other.img2, aUnionFind) ); +}; + + +////////////////////////////////////////////////////////////////////// + + +// rotate: angle image -> image +// Rotates image by angle degrees in a counter-clockwise direction. +// based on http://stackoverflow.com/questions/3276467/adjusting-div-width-and-height-after-rotated +var RotateImage = function(angle, img) { + var sin = Math.sin(angle * Math.PI / 180), + cos = Math.cos(angle * Math.PI / 180); + + // (w,0) rotation + var x1 = Math.floor(cos * img.getWidth()), + y1 = Math.floor(sin * img.getWidth()); + + // (0,h) rotation + var x2 = Math.floor(-sin * img.getHeight()), + y2 = Math.floor( cos * img.getHeight()); + + // (w,h) rotation + var x3 = Math.floor(cos * img.getWidth() - sin * img.getHeight()), + y3 = Math.floor(sin * img.getWidth() + cos * img.getHeight()); + + var minX = Math.min(0, x1, x2, x3), + maxX = Math.max(0, x1, x2, x3), + minY = Math.min(0, y1, y2, y3), + maxY = Math.max(0, y1, y2, y3); + + var rotatedWidth = maxX - minX, + rotatedHeight = maxY - minY; + + // resize the image + BaseImage.call(this, + Math.floor(rotatedWidth / 2), + Math.floor(rotatedHeight / 2)); + + this.img = img; + this.width = rotatedWidth; + this.height = rotatedHeight; + this.angle = angle; + this.translateX = -minX; + this.translateY = -minY; +}; + +RotateImage.prototype = heir(BaseImage.prototype); + + +// translate drawing point, so that this.img appears in the UL corner. Then rotate and render this.img. +RotateImage.prototype.render = function(ctx, x, y) { + ctx.translate(this.translateX, this.translateY); + ctx.rotate(this.angle * Math.PI / 180); + this.img.render(ctx, x, y); + ctx.restore(); +}; + + +RotateImage.prototype.getWidth = function() { + return this.width; +}; + +RotateImage.prototype.getHeight = function() { + return this.height; +}; + +RotateImage.prototype.isEqual = function(other, aUnionFind) { + return ( other instanceof RotateImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.angle == other.angle && + this.translateX == other.translateX && + this.translateY == other.translateY && + types.isEqual(this.img, other.img, aUnionFind) ); +}; + +////////////////////////////////////////////////////////////////////// + + +// ScaleImage: factor factor image -> image +// Scale an image +var ScaleImage = function(xFactor, yFactor, img) { + + // resize the image + BaseImage.call(this, + Math.floor((img.getWidth() * xFactor) / 2), + Math.floor((img.getHeight() * yFactor) / 2)); + + this.img = img; + this.width = img.getWidth() * xFactor; + this.height = img.getHeight() * yFactor; + this.xFactor = xFactor; + this.yFactor = yFactor; +}; + +ScaleImage.prototype = heir(BaseImage.prototype); + + +// scale the context, and pass it to the image's render function +ScaleImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.scale(this.xFactor, this.yFactor); + this.img.render(ctx, x, y); + ctx.restore(); +}; + + +ScaleImage.prototype.getWidth = function() { + return this.width; +}; + +ScaleImage.prototype.getHeight = function() { + return this.height; +}; + +ScaleImage.prototype.isEqual = function(other, aUnionFind) { + return ( other instanceof ScaleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.xFactor == other.xFactor && + this.yFactor == other.yFactor && + types.isEqual(this.img, other.img, aUnionFind) ); +}; + +////////////////////////////////////////////////////////////////////// + + + +var colorString = function(aColor) { + return ("rgb(" + + types.colorRed(aColor) + "," + + types.colorGreen(aColor) + ", " + + types.colorBlue(aColor) + ")"); +}; + + + +var RectangleImage = function(width, height, style, color) { + BaseImage.call(this, width/2, height/2); + this.width = width; + this.height = height; + this.style = style; + this.color = color; +}; +RectangleImage.prototype = heir(BaseImage.prototype); + + +RectangleImage.prototype.render = function(ctx, x, y) { + if (this.style.toString().toLowerCase() == "outline") { + ctx.save(); + ctx.beginPath(); + ctx.strokeStyle = colorString(this.color); + ctx.strokeRect(x, y, this.width, this.height); + ctx.closePath(); + ctx.restore(); + } else { + ctx.save(); + ctx.beginPath(); + + ctx.fillStyle = colorString(this.color); + ctx.fillRect(x, y, this.width, this.height); + + ctx.closePath(); + ctx.restore(); + } +}; + +RectangleImage.prototype.getWidth = function() { + return this.width; +}; + + +RectangleImage.prototype.getHeight = function() { + return this.height; +}; + +RectangleImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof RectangleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + +////////////////////////////////////////////////////////////////////// + +var TextImage = function(msg, size, color) { + BaseImage.call(this, 0, 0); + this.msg = msg; + this.size = size; + this.color = color; + this.font = this.size + "px Optimer"; + + + var canvas = world.Kernel.makeCanvas(0, 0); + var ctx = canvas.getContext("2d"); + ctx.font = this.font; + var metrics = ctx.measureText(msg); + + this.width = metrics.width; + // KLUDGE: I don't know how to get at the height. + this.height = ctx.measureText("m").width + 20; + +} + +TextImage.prototype = heir(BaseImage.prototype); + +TextImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.font = this.font; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + ctx.fillStyle = colorString(this.color); + ctx.fillText(this.msg, x, y); + ctx.restore(); +}; + +TextImage.prototype.getWidth = function() { + return this.width; +}; + + +TextImage.prototype.getHeight = function() { + return this.height; +}; + +TextImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof TextImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.msg == other.msg && + this.size == other.size && + types.isEqual(this.color, other.color, aUnionFind) && + this.font == other.font); +}; + + +////////////////////////////////////////////////////////////////////// + +var CircleImage = function(radius, style, color) { + BaseImage.call(this, radius, radius); + this.radius = radius; + this.style = style; + this.color = color; +} +CircleImage.prototype = heir(BaseImage.prototype); + +CircleImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.beginPath(); + ctx.arc(x + this.radius, + y + this.radius, + this.radius, 0, 2*Math.PI, false); + ctx.closePath(); + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + + ctx.restore(); +}; + +CircleImage.prototype.getWidth = function() { + return this.radius * 2; +}; + +CircleImage.prototype.getHeight = function() { + return this.radius * 2; +}; + +CircleImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof CircleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.radius == other.radius && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + +////////////////////////////////////////////////////////////////////// + + +// StarImage: fixnum fixnum fixnum color -> image +var StarImage = function(points, outer, inner, style, color) { + BaseImage.call(this, + Math.max(outer, inner), + Math.max(outer, inner)); + this.points = points; + this.outer = outer; + this.inner = inner; + this.style = style; + this.color = color; + + this.radius = Math.max(this.inner, this.outer); +}; + +StarImage.prototype = heir(BaseImage.prototype); + +var oneDegreeAsRadian = Math.PI / 180; + +// render: context fixnum fixnum -> void +// Draws a star on the given context. +// Most of this code here adapted from the Canvas tutorial at: +// http://developer.apple.com/safari/articles/makinggraphicswithcanvas.html +StarImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.beginPath(); + for( var pt = 0; pt < (this.points * 2) + 1; pt++ ) { + var rads = ( ( 360 / (2 * this.points) ) * pt ) * oneDegreeAsRadian - 0.5; + var radius = ( pt % 2 == 1 ) ? this.outer : this.inner; + ctx.lineTo(x + this.radius + ( Math.sin( rads ) * radius ), + y + this.radius + ( Math.cos( rads ) * radius ) ); + } + ctx.closePath(); + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + + ctx.restore(); +}; + +// getWidth: -> fixnum +StarImage.prototype.getWidth = function() { + return this.radius * 2; +}; + + +// getHeight: -> fixnum +StarImage.prototype.getHeight = function() { + return this.radius * 2; +}; + +StarImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof StarImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.points == other.points && + this.outer == other.outer && + this.inner == other.inner && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + + +////////////////////////////////////////////////////////////////////// +//Triangle +/////// +var TriangleImage = function(side, style, color) { + this.width = side; + this.height = Math.ceil(side * Math.sqrt(3) / 2); + + BaseImage.call(this, Math.floor(this.width/2), Math.floor(this.height/2)); + this.side = side; + this.style = style; + this.color = color; +} +TriangleImage.prototype = heir(BaseImage.prototype); + + +TriangleImage.prototype.render = function(ctx, x, y) { + var width = this.getWidth(); + var height = this.getHeight(); + ctx.save(); + ctx.beginPath(); + ctx.moveTo(x + this.side/2, y); + ctx.lineTo(x + width, y + height); + ctx.lineTo(x, y + height); + ctx.closePath(); + + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } + else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + ctx.restore(); +}; + + + +TriangleImage.prototype.getWidth = function() { + return this.width; +}; + +TriangleImage.prototype.getHeight = function() { + return this.height; +}; + +TriangleImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof TriangleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.side == other.side && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + +////////////////////////////////////////////////////////////////////// +//Ellipse +var EllipseImage = function(width, height, style, color) { + BaseImage.call(this, Math.floor(width/2), Math.floor(height/2)); + this.width = width; + this.height = height; + this.style = style; + this.color = color; +}; + +EllipseImage.prototype = heir(BaseImage.prototype); + + +EllipseImage.prototype.render = function(ctx, aX, aY) { + ctx.save(); + ctx.beginPath(); + + // Most of this code is taken from: + // http://webreflection.blogspot.com/2009/01/ellipse-and-circle-for-canvas-2d.html + var hB = (this.width / 2) * .5522848, + vB = (this.height / 2) * .5522848, + eX = aX + this.width, + eY = aY + this.height, + mX = aX + this.width / 2, + mY = aY + this.height / 2; + ctx.moveTo(aX, mY); + ctx.bezierCurveTo(aX, mY - vB, mX - hB, aY, mX, aY); + ctx.bezierCurveTo(mX + hB, aY, eX, mY - vB, eX, mY); + ctx.bezierCurveTo(eX, mY + vB, mX + hB, eY, mX, eY); + ctx.bezierCurveTo(mX - hB, eY, aX, mY + vB, aX, mY); + ctx.closePath(); + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } + else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + + + ctx.restore(); +}; + +EllipseImage.prototype.getWidth = function() { + return this.width; +}; + +EllipseImage.prototype.getHeight = function() { + return this.height; +}; + +EllipseImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof EllipseImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + +////////////////////////////////////////////////////////////////////// +//Line +var LineImage = function(x, y, color) { + if (x >= 0) { + if (y >= 0) { + BaseImage.call(this, 0, 0); + } else { + BaseImage.call(this, 0, -y); + } + } else { + if (y >= 0) { + BaseImage.call(this, -x, 0); + } else { + BaseImage.call(this, -x, -y); + } + } + + + this.x = x; + this.y = y; + this.color = color; + this.width = Math.abs(x) + 1; + this.height = Math.abs(y) + 1; +} + +LineImage.prototype = heir(BaseImage.prototype); + + +LineImage.prototype.render = function(ctx, xstart, ystart) { + ctx.save(); + + if (this.x >= 0) { + if (this.y >= 0) { + ctx.moveTo(xstart, ystart); + ctx.lineTo((xstart + this.x), + (ystart + this.y)); + } else { + ctx.moveTo(xstart, ystart + (-this.y)); + ctx.lineTo(xstart + this.x, ystart); + } + } else { + if (this.y >= 0) { + ctx.moveTo(xstart + (-this.x), ystart); + ctx.lineTo(xstart, + (ystart + this.y)); + } else { + ctx.moveTo(xstart + (-this.x), ystart + (-this.y)); + ctx.lineTo(xstart, ystart); + } + } + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + ctx.restore(); +}; + + +LineImage.prototype.getWidth = function() { + return this.width; +}; + + +LineImage.prototype.getHeight = function() { + return this.height; +}; + +LineImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof LineImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.x == other.x && + this.y == other.y && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + + + +////////////////////////////////////////////////////////////////////// +// Effects + +/** + * applyEffect: compound-effect -> (arrayof (world -> world)) + + applyEffect applies all of the effects + + @param aCompEffect a compound effect is either a scheme list of + compound effects or a single primitive effect */ +world.Kernel.applyEffect = function(aCompEffect) { + if ( types.isEmpty(aCompEffect) ) { + // Do Nothing + } else if ( types.isPair(aCompEffect) ) { + var results = world.Kernel.applyEffect(aCompEffect.first()); + return results.concat(world.Kernel.applyEffect(aCompEffect.rest())); + } else { + var newResult = aCompEffect.run(); + if (newResult) { + return newResult; + } + } + return []; +} + +////////////////////////////////////////////////////////////////////////// + + + + +// Color database +var ColorDb = function() { + this.colors = {}; +} +ColorDb.prototype.put = function(name, color) { + this.colors[name] = color; +}; + +ColorDb.prototype.get = function(name) { + return this.colors[name.toString().toUpperCase()]; +}; + + +// FIXME: update toString to handle the primitive field values. + +var colorDb = new ColorDb(); +colorDb.put("ORANGE", types.color(255, 165, 0)); +colorDb.put("RED", types.color(255, 0, 0)); +colorDb.put("ORANGERED", types.color(255, 69, 0)); +colorDb.put("TOMATO", types.color(255, 99, 71)); +colorDb.put("DARKRED", types.color(139, 0, 0)); +colorDb.put("RED", types.color(255, 0, 0)); +colorDb.put("FIREBRICK", types.color(178, 34, 34)); +colorDb.put("CRIMSON", types.color(220, 20, 60)); +colorDb.put("DEEPPINK", types.color(255, 20, 147)); +colorDb.put("MAROON", types.color(176, 48, 96)); +colorDb.put("INDIAN RED", types.color(205, 92, 92)); +colorDb.put("INDIANRED", types.color(205, 92, 92)); +colorDb.put("MEDIUM VIOLET RED", types.color(199, 21, 133)); +colorDb.put("MEDIUMVIOLETRED", types.color(199, 21, 133)); +colorDb.put("VIOLET RED", types.color(208, 32, 144)); +colorDb.put("VIOLETRED", types.color(208, 32, 144)); +colorDb.put("LIGHTCORAL", types.color(240, 128, 128)); +colorDb.put("HOTPINK", types.color(255, 105, 180)); +colorDb.put("PALEVIOLETRED", types.color(219, 112, 147)); +colorDb.put("LIGHTPINK", types.color(255, 182, 193)); +colorDb.put("ROSYBROWN", types.color(188, 143, 143)); +colorDb.put("PINK", types.color(255, 192, 203)); +colorDb.put("ORCHID", types.color(218, 112, 214)); +colorDb.put("LAVENDERBLUSH", types.color(255, 240, 245)); +colorDb.put("SNOW", types.color(255, 250, 250)); +colorDb.put("CHOCOLATE", types.color(210, 105, 30)); +colorDb.put("SADDLEBROWN", types.color(139, 69, 19)); +colorDb.put("BROWN", types.color(132, 60, 36)); +colorDb.put("DARKORANGE", types.color(255, 140, 0)); +colorDb.put("CORAL", types.color(255, 127, 80)); +colorDb.put("SIENNA", types.color(160, 82, 45)); +colorDb.put("ORANGE", types.color(255, 165, 0)); +colorDb.put("SALMON", types.color(250, 128, 114)); +colorDb.put("PERU", types.color(205, 133, 63)); +colorDb.put("DARKGOLDENROD", types.color(184, 134, 11)); +colorDb.put("GOLDENROD", types.color(218, 165, 32)); +colorDb.put("SANDYBROWN", types.color(244, 164, 96)); +colorDb.put("LIGHTSALMON", types.color(255, 160, 122)); +colorDb.put("DARKSALMON", types.color(233, 150, 122)); +colorDb.put("GOLD", types.color(255, 215, 0)); +colorDb.put("YELLOW", types.color(255, 255, 0)); +colorDb.put("OLIVE", types.color(128, 128, 0)); +colorDb.put("BURLYWOOD", types.color(222, 184, 135)); +colorDb.put("TAN", types.color(210, 180, 140)); +colorDb.put("NAVAJOWHITE", types.color(255, 222, 173)); +colorDb.put("PEACHPUFF", types.color(255, 218, 185)); +colorDb.put("KHAKI", types.color(240, 230, 140)); +colorDb.put("DARKKHAKI", types.color(189, 183, 107)); +colorDb.put("MOCCASIN", types.color(255, 228, 181)); +colorDb.put("WHEAT", types.color(245, 222, 179)); +colorDb.put("BISQUE", types.color(255, 228, 196)); +colorDb.put("PALEGOLDENROD", types.color(238, 232, 170)); +colorDb.put("BLANCHEDALMOND", types.color(255, 235, 205)); +colorDb.put("MEDIUM GOLDENROD", types.color(234, 234, 173)); +colorDb.put("MEDIUMGOLDENROD", types.color(234, 234, 173)); +colorDb.put("PAPAYAWHIP", types.color(255, 239, 213)); +colorDb.put("MISTYROSE", types.color(255, 228, 225)); +colorDb.put("LEMONCHIFFON", types.color(255, 250, 205)); +colorDb.put("ANTIQUEWHITE", types.color(250, 235, 215)); +colorDb.put("CORNSILK", types.color(255, 248, 220)); +colorDb.put("LIGHTGOLDENRODYELLOW", types.color(250, 250, 210)); +colorDb.put("OLDLACE", types.color(253, 245, 230)); +colorDb.put("LINEN", types.color(250, 240, 230)); +colorDb.put("LIGHTYELLOW", types.color(255, 255, 224)); +colorDb.put("SEASHELL", types.color(255, 245, 238)); +colorDb.put("BEIGE", types.color(245, 245, 220)); +colorDb.put("FLORALWHITE", types.color(255, 250, 240)); +colorDb.put("IVORY", types.color(255, 255, 240)); +colorDb.put("GREEN", types.color(0, 255, 0)); +colorDb.put("LAWNGREEN", types.color(124, 252, 0)); +colorDb.put("CHARTREUSE", types.color(127, 255, 0)); +colorDb.put("GREEN YELLOW", types.color(173, 255, 47)); +colorDb.put("GREENYELLOW", types.color(173, 255, 47)); +colorDb.put("YELLOW GREEN", types.color(154, 205, 50)); +colorDb.put("YELLOWGREEN", types.color(154, 205, 50)); +colorDb.put("MEDIUM FOREST GREEN", types.color(107, 142, 35)); +colorDb.put("OLIVEDRAB", types.color(107, 142, 35)); +colorDb.put("MEDIUMFORESTGREEN", types.color(107, 142, 35)); +colorDb.put("DARK OLIVE GREEN", types.color(85, 107, 47)); +colorDb.put("DARKOLIVEGREEN", types.color(85, 107, 47)); +colorDb.put("DARKSEAGREEN", types.color(143, 188, 139)); +colorDb.put("LIME", types.color(0, 255, 0)); +colorDb.put("DARK GREEN", types.color(0, 100, 0)); +colorDb.put("DARKGREEN", types.color(0, 100, 0)); +colorDb.put("LIME GREEN", types.color(50, 205, 50)); +colorDb.put("LIMEGREEN", types.color(50, 205, 50)); +colorDb.put("FOREST GREEN", types.color(34, 139, 34)); +colorDb.put("FORESTGREEN", types.color(34, 139, 34)); +colorDb.put("SPRING GREEN", types.color(0, 255, 127)); +colorDb.put("SPRINGGREEN", types.color(0, 255, 127)); +colorDb.put("MEDIUM SPRING GREEN", types.color(0, 250, 154)); +colorDb.put("MEDIUMSPRINGGREEN", types.color(0, 250, 154)); +colorDb.put("SEA GREEN", types.color(46, 139, 87)); +colorDb.put("SEAGREEN", types.color(46, 139, 87)); +colorDb.put("MEDIUM SEA GREEN", types.color(60, 179, 113)); +colorDb.put("MEDIUMSEAGREEN", types.color(60, 179, 113)); +colorDb.put("AQUAMARINE", types.color(112, 216, 144)); +colorDb.put("LIGHTGREEN", types.color(144, 238, 144)); +colorDb.put("PALE GREEN", types.color(152, 251, 152)); +colorDb.put("PALEGREEN", types.color(152, 251, 152)); +colorDb.put("MEDIUM AQUAMARINE", types.color(102, 205, 170)); +colorDb.put("MEDIUMAQUAMARINE", types.color(102, 205, 170)); +colorDb.put("TURQUOISE", types.color(64, 224, 208)); +colorDb.put("LIGHTSEAGREEN", types.color(32, 178, 170)); +colorDb.put("MEDIUM TURQUOISE", types.color(72, 209, 204)); +colorDb.put("MEDIUMTURQUOISE", types.color(72, 209, 204)); +colorDb.put("HONEYDEW", types.color(240, 255, 240)); +colorDb.put("MINTCREAM", types.color(245, 255, 250)); +colorDb.put("ROYALBLUE", types.color(65, 105, 225)); +colorDb.put("DODGERBLUE", types.color(30, 144, 255)); +colorDb.put("DEEPSKYBLUE", types.color(0, 191, 255)); +colorDb.put("CORNFLOWERBLUE", types.color(100, 149, 237)); +colorDb.put("STEEL BLUE", types.color(70, 130, 180)); +colorDb.put("STEELBLUE", types.color(70, 130, 180)); +colorDb.put("LIGHTSKYBLUE", types.color(135, 206, 250)); +colorDb.put("DARK TURQUOISE", types.color(0, 206, 209)); +colorDb.put("DARKTURQUOISE", types.color(0, 206, 209)); +colorDb.put("CYAN", types.color(0, 255, 255)); +colorDb.put("AQUA", types.color(0, 255, 255)); +colorDb.put("DARKCYAN", types.color(0, 139, 139)); +colorDb.put("TEAL", types.color(0, 128, 128)); +colorDb.put("SKY BLUE", types.color(135, 206, 235)); +colorDb.put("SKYBLUE", types.color(135, 206, 235)); +colorDb.put("CADET BLUE", types.color(96, 160, 160)); +colorDb.put("CADETBLUE", types.color(95, 158, 160)); +colorDb.put("DARK SLATE GRAY", types.color(47, 79, 79)); +colorDb.put("DARKSLATEGRAY", types.color(47, 79, 79)); +colorDb.put("LIGHTSLATEGRAY", types.color(119, 136, 153)); +colorDb.put("SLATEGRAY", types.color(112, 128, 144)); +colorDb.put("LIGHT STEEL BLUE", types.color(176, 196, 222)); +colorDb.put("LIGHTSTEELBLUE", types.color(176, 196, 222)); +colorDb.put("LIGHT BLUE", types.color(173, 216, 230)); +colorDb.put("LIGHTBLUE", types.color(173, 216, 230)); +colorDb.put("POWDERBLUE", types.color(176, 224, 230)); +colorDb.put("PALETURQUOISE", types.color(175, 238, 238)); +colorDb.put("LIGHTCYAN", types.color(224, 255, 255)); +colorDb.put("ALICEBLUE", types.color(240, 248, 255)); +colorDb.put("AZURE", types.color(240, 255, 255)); +colorDb.put("MEDIUM BLUE", types.color(0, 0, 205)); +colorDb.put("MEDIUMBLUE", types.color(0, 0, 205)); +colorDb.put("DARKBLUE", types.color(0, 0, 139)); +colorDb.put("MIDNIGHT BLUE", types.color(25, 25, 112)); +colorDb.put("MIDNIGHTBLUE", types.color(25, 25, 112)); +colorDb.put("NAVY", types.color(36, 36, 140)); +colorDb.put("BLUE", types.color(0, 0, 255)); +colorDb.put("INDIGO", types.color(75, 0, 130)); +colorDb.put("BLUE VIOLET", types.color(138, 43, 226)); +colorDb.put("BLUEVIOLET", types.color(138, 43, 226)); +colorDb.put("MEDIUM SLATE BLUE", types.color(123, 104, 238)); +colorDb.put("MEDIUMSLATEBLUE", types.color(123, 104, 238)); +colorDb.put("SLATE BLUE", types.color(106, 90, 205)); +colorDb.put("SLATEBLUE", types.color(106, 90, 205)); +colorDb.put("PURPLE", types.color(160, 32, 240)); +colorDb.put("DARK SLATE BLUE", types.color(72, 61, 139)); +colorDb.put("DARKSLATEBLUE", types.color(72, 61, 139)); +colorDb.put("DARKVIOLET", types.color(148, 0, 211)); +colorDb.put("DARK ORCHID", types.color(153, 50, 204)); +colorDb.put("DARKORCHID", types.color(153, 50, 204)); +colorDb.put("MEDIUMPURPLE", types.color(147, 112, 219)); +colorDb.put("CORNFLOWER BLUE", types.color(68, 64, 108)); +colorDb.put("MEDIUM ORCHID", types.color(186, 85, 211)); +colorDb.put("MEDIUMORCHID", types.color(186, 85, 211)); +colorDb.put("MAGENTA", types.color(255, 0, 255)); +colorDb.put("FUCHSIA", types.color(255, 0, 255)); +colorDb.put("DARKMAGENTA", types.color(139, 0, 139)); +colorDb.put("VIOLET", types.color(238, 130, 238)); +colorDb.put("PLUM", types.color(221, 160, 221)); +colorDb.put("LAVENDER", types.color(230, 230, 250)); +colorDb.put("THISTLE", types.color(216, 191, 216)); +colorDb.put("GHOSTWHITE", types.color(248, 248, 255)); +colorDb.put("WHITE", types.color(255, 255, 255)); +colorDb.put("WHITESMOKE", types.color(245, 245, 245)); +colorDb.put("GAINSBORO", types.color(220, 220, 220)); +colorDb.put("LIGHT GRAY", types.color(211, 211, 211)); +colorDb.put("LIGHTGRAY", types.color(211, 211, 211)); +colorDb.put("SILVER", types.color(192, 192, 192)); +colorDb.put("GRAY", types.color(190, 190, 190)); +colorDb.put("DARK GRAY", types.color(169, 169, 169)); +colorDb.put("DARKGRAY", types.color(169, 169, 169)); +colorDb.put("DIM GRAY", types.color(105, 105, 105)); +colorDb.put("DIMGRAY", types.color(105, 105, 105)); +colorDb.put("BLACK", types.color(0, 0, 0)); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////// +// Exports + +world.Kernel.isImage = isImage; +world.Kernel.isScene = isScene; +world.Kernel.isColor = function(thing) { + return (types.isColor(thing) || + ((types.isString(thing) || types.isSymbol(thing)) && + typeof(colorDb.get(thing)) != 'undefined')); +}; +world.Kernel.colorDb = colorDb; + +world.Kernel.sceneImage = function(width, height, children, withBorder) { + return new SceneImage(width, height, children, withBorder); +}; +world.Kernel.circleImage = function(radius, style, color) { + return new CircleImage(radius, style, color); +}; +world.Kernel.starImage = function(points, outer, inner, style, color) { + return new StarImage(points, outer, inner, style, color); +}; +world.Kernel.rectangleImage = function(width, height, style, color) { + return new RectangleImage(width, height, style, color); +}; +world.Kernel.triangleImage = function(side, style, color) { + return new TriangleImage(side, style, color); +}; +world.Kernel.ellipseImage = function(width, height, style, color) { + return new EllipseImage(width, height, style, color); +}; +world.Kernel.lineImage = function(x, y, color) { + return new LineImage(x, y, color); +}; +world.Kernel.overlayImage = function(img1, img2, shiftX, shiftY) { + return new OverlayImage(img1, img2, shiftX, shiftY); +}; +world.Kernel.rotateImage = function(angle, img) { + return new RotateImage(angle, img); +}; +world.Kernel.scaleImage = function(xFactor, yFactor, img) { + return new ScaleImage(xFactor, yFactor, img); +}; +world.Kernel.textImage = function(msg, size, color) { + return new TextImage(msg, size, color); +}; +world.Kernel.fileImage = function(path, rawImage) { + return FileImage.makeInstance(path, rawImage); +}; + + +world.Kernel.isSceneImage = function(x) { return x instanceof SceneImage; }; +world.Kernel.isCircleImage = function(x) { return x instanceof CircleImage; }; +world.Kernel.isStarImage = function(x) { return x instanceof StarImage; }; +world.Kernel.isRectangleImage = function(x) { return x instanceof RectangleImage; }; +world.Kernel.isTriangleImage = function(x) { return x instanceof TriangleImage; }; +world.Kernel.isEllipseImage = function(x) { return x instanceof EllipseImage; }; +world.Kernel.isLineImage = function(x) { return x instanceof LineImage; }; +world.Kernel.isOverlayImage = function(x) { return x instanceof OverlayImage; }; +world.Kernel.isRotateImage = function(x) { return x instanceof RotateImage; }; +world.Kernel.isTextImage = function(x) { return x instanceof TextImage; }; +world.Kernel.isFileImage = function(x) { return x instanceof FileImage; }; + + + + + + + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + + +// Feeds stimuli inputs into the world. The functions here +// are responsible for converting to Scheme values. +// +// NOTE and WARNING: make sure to really do the coersions, even for +// strings. Bad things happen otherwise, as in the sms stuff, where +// we're getting string-like values that aren't actually strings. + + + +world.stimuli = {}; +world.Kernel.stimuli = world.stimuli; + + +(function() { + var handlers = []; + + var doNothing = function() {}; + + + var StimuliHandler = function(config, caller, restarter) { + this.config = config; + this.caller = caller; + this.restarter = restarter; + handlers.push(this); + }; + + // StimuliHandler.prototype.failHandler = function(e) { + // this.onShutdown(); + // this.restarter(e); + // }; + + // doStimuli: CPS( (world -> effect) (world -> world) -> void ) + // + // Processes a stimuli by compute the effect and applying it, and + // computing a new world to replace the old. + StimuliHandler.prototype.doStimuli = function(computeEffectF, computeWorldF, restArgs, k) { + var effectUpdaters = []; + var that = this; + try { + that.change(function(w, k2) { + var args = [w].concat(restArgs); + var doStimuliHelper = function() { + if (computeWorldF) { + that.caller(computeWorldF, args, k2); + } else { + k2(w); + } + }; + doStimuliHelper(); + }, k); + // if (computeEffectF) { + // that.caller(computeEffectF, [args], + // function(effect) { + // effectUpdaters = applyEffect(effect); + // doStimuliHelper(); + // }, + // this.failHandler); + // } + // else { doStimuliHelper(); } + // }, + // function() { + // helpers.forEachK(effectUpdaters, + // function(effect, k2) { that.change(effect, k2); }, + // function(e) { throw e; }, + // k); + // }); + } catch (e) { + // if (console && console.log && e.stack) { + // console.log(e.stack); + // } + this.onShutdown(); + } + } + + + // Orientation change + // args: [azimuth, pitch, roll] + StimuliHandler.prototype.onTilt = function(args, k) { + var onTilt = this.lookup("onTilt"); + var onTiltEffect = this.lookup("onTiltEffect"); + this.doStimuli(onTiltEffect, onTilt, helpers.map(flt, args), k); + }; + + + // Accelerations + // args: [x, y, z] + StimuliHandler.prototype.onAcceleration = function(args, k) { + var onAcceleration = this.lookup('onAcceleration'); + var onAccelerationEffect = this.lookup('onAccelerationEffect'); + this.doStimuli(onAccelerationEffect, onAcceleration, helpers.map(flt, args), k); + }; + + + // Shakes + // args: [] + StimuliHandler.prototype.onShake = function(args, k) { + var onShake = this.lookup('onShake'); + var onShakeEffect = this.lookup('onShakeEffect'); + this.doStimuli(onShakeEffect, onShake, [], k); + }; + + + // Sms receiving + // args: [sender, message] + StimuliHandler.prototype.onSmsReceive = function(args, k) { + var onSmsReceive = this.lookup('onSmsReceive'); + var onSmsReceiveEffect = this.lookup('onSmsReceiveEffect'); + // IMPORTANT: must coerse to string by using x+"". Do not use + // toString(): it's not safe. + this.doStimuli(onSmsReceiveEffect, onSmsReceive, [args[0]+"", args[1]+""], k); + }; + + + // Locations + // args: [lat, lng] + StimuliHandler.prototype.onLocation = function(args, k) { + var onLocationChange = this.lookup('onLocationChange'); + var onLocationChangeEffect = this.lookup('onLocationChangeEffect'); + this.doStimuli(onLocationChangeEffect, onLocationChange, helpers.map(flt, args), k); + }; + + + + // Keystrokes + // args: [e] + StimuliHandler.prototype.onKey = function(args, k) { + // getKeyCodeName: keyEvent -> String + // Given an event, try to get the name of the key. + var getKeyCodeName = function(e) { + var code = e.charCode || e.keyCode; + var keyname; + switch(code) { + case 16: keyname = "shift"; break; + case 17: keyname = "control"; break; + case 19: keyname = "pause"; break; + case 27: keyname = "escape"; break; + case 33: keyname = "prior"; break; + case 34: keyname = "next"; break; + case 35: keyname = "end"; break; + case 36: keyname = "home"; break; + case 37: keyname = "left"; break; + case 38: keyname = "up"; break; + case 39: keyname = "right"; break; + case 40: keyname = "down"; break; + case 42: keyname = "print"; break; + case 45: keyname = "insert"; break; + case 46: keyname = String.fromCharCode(127); break; + case 106: keyname = "*"; break; + case 107: keyname = "+"; break; + case 109: keyname = "-"; break; + case 110: keyname = "."; break; + case 111: keyname = "/"; break; + case 144: keyname = "numlock"; break; + case 145: keyname = "scroll"; break; + case 186: keyname = ";"; break; + case 187: keyname = "="; break; + case 188: keyname = ","; break; + case 189: keyname = "-"; break; + case 190: keyname = "."; break; + case 191: keyname = "/"; break; + case 192: keyname = "`"; break; + case 219: keyname = "["; break; + case 220: keyname = "\\"; break; + case 221: keyname = "]"; break; + case 222: keyname = "'"; break; + default: if (code >= 96 && code <= 105) { + keyname = (code - 96).toString(); + } + else if (code >= 112 && code <= 123) { + keyname = "f" + (code - 111); + } + else { + keyname = String.fromCharCode(code).toLowerCase(); + } + break; + } + return keyname; + } + var keyname = getKeyCodeName(args[0]); + var onKey = this.lookup('onKey'); + var onKeyEffect = this.lookup('onKeyEffect'); + this.doStimuli(onKeyEffect, onKey, [keyname], k); + }; + + + + // // Time ticks + // // args: [] + // StimuliHandler.prototype.onTick = function(args, k) { + // var onTick = this.lookup('onTick'); + // var onTickEffect = this.lookup('onTickEffect'); + // this.doStimuli(onTickEffect, onTick, [], k); + // }; + + + + // Announcements + // args: [eventName, vals] + StimuliHandler.prototype.onAnnounce = function(args, k) { + var vals = args[1]; + var valsList = types.EMPTY; + for (var i = 0; i < vals.length; i++) { + valsList = types.cons(vals[vals.length - i - 1], valsList); + } + + var onAnnounce = this.lookup('onAnnounce'); + var onAnnounceEffect = this.lookup('onAnnounceEffect'); + this.doStimuli(onAnnounce, onAnnounceEffect, [args[0], valsList], k); + }; + + + + // The shutdown stimuli: special case that forces a world computation to quit. + // Also removes this instance from the list of handlers + StimuliHandler.prototype.onShutdown = function() { + var index = handlers.indexOf(this); + if (index != -1) { + handlers.splice(index, 1); + } + + var shutdownWorld = this.lookup('shutdownWorld'); + if (shutdownWorld) { + shutdownWorld(); + } + }; + + + ////////////////////////////////////////////////////////////////////// + // Helpers + var flt = types.float; + + StimuliHandler.prototype.lookup = function(s) { + return this.config.lookup(s); + }; + + StimuliHandler.prototype.change = function(f, k) { + if (this.lookup('changeWorld')) { + this.lookup('changeWorld')(f, k); + } + else { k(); } + }; + + // applyEffect: compound-effect: (arrayof (world -> world)) + var applyEffect = function(e) { + return world.Kernel.applyEffect(e); + }; + + var makeStimulusHandler = function(funName) { + return function() { + var args = arguments; + for (var i = 0; i < handlers.length; i++) { + (handlers[i])[funName](args, doNothing); + } + // helpers.forEachK(handlers, + // function(h, k) { h[funName](args, k); }, + // function(e) { throw e; }, + // doNothing); + } + }; + + ////////////////////////////////////////////////////////////////////// + // Exports + + world.stimuli.StimuliHandler = StimuliHandler; + + world.stimuli.onTilt = makeStimulusHandler('onTilt'); + world.stimuli.onAcceleration = makeStimulusHandler('onAcceleration'); + world.stimuli.onShake = makeStimulusHandler('onShake'); + world.stimuli.onSmsReceive = makeStimulusHandler('onSmsReceive'); + world.stimuli.onLocation = makeStimulusHandler('onLocation'); + world.stimuli.onKey = makeStimulusHandler('onKey'); + // world.stimuli.onTick = makeStimulusHandler('onTick'); + world.stimuli.onAnnounce = makeStimulusHandler('onAnnounce'); + + world.stimuli.massShutdown = function() { + for (var i = 0; i < handlers.length; i++) { + var shutdownWorld = handlers[i].lookup('shutdownWorld'); + if (shutdownWorld) { + shutdownWorld(); + } + } + handlers = []; + }; + + +})(); + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + + + + + + + +(function() { + +// var make_dash_effect_colon_none = +// (plt.Kernel.invokeModule("moby/runtime/effect-struct") +// .EXPORTS['make-effect:none']); + + world.config = {}; + world.Kernel.config = world.config; + + + // augment: hash hash -> hash + // Functionally extend a hashtable with another one. + var augment = function(o, a) { + var oo = {}; + for (var e in o) { + if (o.hasOwnProperty(e)) { + oo[e] = o[e]; + } + } + for (var e in a) { + if (a.hasOwnProperty(e)) { + oo[e] = a[e]; + } + } + return oo; + } + + + + var WorldConfig = function() { + // The following handler values are initially false until they're updated + // by configuration. + + // A handler is a function: + // handler: world X Y ... -> Z + + + this.vals = { + // changeWorld: (world -> world) -> void + // When called, this will update the world based on the + // updater passed to it. + changeWorld: false, + + // shutdownWorld: -> void + // When called, this will shut down the world computation. + shutdownWorld: false, + + // initialEffect: effect + // The initial effect to invoke when the world computation + // begins. + initialEffect: false, + + + // onRedraw: world -> scene + onRedraw: false, + + // onDraw: world -> (sexpof dom) + onDraw: false, + + // onDrawCss: world -> (sexpof css-style) + onDrawCss: false, + + + // tickDelay: number + tickDelay: false, + // onTick: world -> world + onTick: false, + // onTickEffect: world -> effect + onTickEffect: false, + + // onKey: world key -> world + onKey: false, + // onKeyEffect: world key -> effect + onKeyEffect : false, + + // onTilt: world number number number -> world + onTilt: false, + // onTiltEffect: world number number number -> effect + onTiltEffect: false, + + // onAcceleration: world number number number -> world + onAcceleration: false, + // onAccelerationEffect: world number number number -> effect + onAccelerationEffect: false, + + // onShake: world -> world + onShake: false, + // onShakeEffect: world -> effect + onShakeEffect: false, + + // onSmsReceive: world -> world + onSmsReceive: false, + // onSmsReceiveEffect: world -> effect + onSmsReceiveEffect: false, + + // onLocationChange: world number number -> world + onLocationChange : false, + // onLocationChangeEffect: world number number -> effect + onLocationChangeEffect: false, + + + // onAnnounce: world string X ... -> world + onAnnounce: false, + // onAnnounce: world string X ... -> effect + onAnnounceEffect: false, + + // stopWhen: world -> boolean + stopWhen: false, + // stopWhenEffect: world -> effect + stopWhenEffect: false, + + + + ////////////////////////////////////////////////////////////////////// + // For universe game playing + + // connectToGame: string + // Registers with some universe, given an identifier + // which is a URL to a Universe server. + connectToGame: false, + onGameStart: false, + onOpponentTurn: false, + onMyTurn: false, + afterMyTurn: false, + onGameFinish: false + }; + } + + + // WorldConfig.lookup: string -> handler + // Looks up a value in the configuration. + WorldConfig.prototype.lookup = function(key) { +// plt.Kernel.check(key, plt.Kernel.isString, "WorldConfig.lookup", "string", 1); + if (key in this.vals) { + return this.vals[key]; + } else { + throw Error("Can't find " + key + " in the configuration"); + } + } + + + + // WorldConfig.updateAll: (hashof string handler) -> WorldConfig + WorldConfig.prototype.updateAll = function(aHash) { + var result = new WorldConfig(); + result.vals = augment(this.vals, aHash); + return result; + } + + + world.config.WorldConfig = WorldConfig; + + // The following global variable CONFIG is mutated by either + // big-bang from the regular world or the one in jsworld. + world.config.CONFIG = new WorldConfig(); + + + // A handler is a function that consumes a config and produces a + // config. + + + ////////////////////////////////////////////////////////////////////// + + var getNoneEffect = function() { + throw new Error("getNoneEffect: We should not be calling effects!"); + // return make_dash_effect_colon_none(); + } + + + + ////////////////////////////////////////////////////////////////////// + + world.config.Kernel = world.config.Kernel || {}; + world.config.Kernel.getNoneEffect = getNoneEffect; + + +/* + // makeSimplePropertyUpdater: (string (X -> boolean) string string) -> (X -> handler) + var makeSimplePropertyUpdater = function(propertyName, + propertyPredicate, + propertyTypeName, + updaterName) { + return function(val) { + plt.Kernel.check(val, propertyPredicate, updaterName, propertyTypeName, 1); + return addStringMethods( + function(config) { + return config.updateAll({propertyName: val }); + }, updaterName); + } + }; + + // connects to the game + world.config.Kernel.connect_dash_to_dash_game = + makeSimplePropertyUpdater('connectToGame', + plt.Kernel.isString, + "string", + "connect-to-game"); + + + // Registers a handler for game-start events. + world.config.Kernel.on_dash_game_dash_start = + makeSimplePropertyUpdater('onGameStart', + plt.Kernel.isFunction, + "function", + "on-game-start"); + + + // Registers a handler for opponent-turn events. + world.config.Kernel.on_dash_opponent_dash_turn = + makeSimplePropertyUpdater('onOpponentTurn', + plt.Kernel.isFunction, + "function", + "on-opponent-turn"); + + + // Registers a handler for my turn. + world.config.Kernel.on_dash_my_dash_turn = + makeSimplePropertyUpdater('onMyTurn', + plt.Kernel.isFunction, + "function", + "on-my-turn"); + + // Register a handler after I make a move. + world.config.Kernel.after_dash_my_dash_turn = + makeSimplePropertyUpdater('afterMyTurn', + plt.Kernel.isFunction, + "function", + "after-my-turn"); + + world.config.Kernel.on_dash_game_dash_finish = + makeSimplePropertyUpdater('onGameFinish', + plt.Kernel.isFunction, + "function", + "on-game-finish"); +*/ + + + +})(); diff --git a/world/scratch/world/kernel.rkt b/world/scratch/world/kernel.rkt new file mode 100644 index 0000000..f315428 --- /dev/null +++ b/world/scratch/world/kernel.rkt @@ -0,0 +1,3 @@ +#lang s-exp "../lang/js-impl/js-impl.rkt" + +(require-js "kernel.js")