var imageLibrary = MACHINE.modules['whalesong/image/private/main.rkt'].privateExports; var isImage = imageLibrary.isImage; var PAUSE = plt.runtime.PAUSE; var EMPTY = plt.baselib.lists.EMPTY; var isString = plt.baselib.strings.isString; var isBoolean = function(x) { return x === true || x === false; } var isSymbol = plt.baselib.symbols.isSymbol; var makePair = plt.baselib.lists.makePair; var makeList = plt.baselib.lists.makeList; var makeRational = plt.baselib.numbers.makeRational; var finalizeClosureCall = plt.baselib.functions.finalizeClosureCall; ////////////////////////////////////////////////////////////////////// var bigBang = function(MACHINE, initW, handlers) { var outerToplevelNode = $('').css('padding', '0px').get(0); MACHINE.params.currentOutputPort.writeDomNode(MACHINE, outerToplevelNode); var toplevelNode = $('').css('padding', '0px').appendTo(outerToplevelNode).get(0); var configs = []; var isOutputConfigSeen = false; for (var i = 0 ; i < handlers.length; i++) { if (isWorldConfigOption(handlers[i])) { configs.push(handlers[i].toRawHandler(MACHINE, toplevelNode)); } else { configs.push(handlers[i]); } if (isOutputConfig(handlers[i])) { isOutputConfigSeen = true; } } // If we haven't seen an onDraw function, use the default one. if (! isOutputConfigSeen) { configs.push(new DefaultDrawingOutput().toRawHandler(MACHINE, toplevelNode)); } PAUSE(function(restart) { // var onBreak = function() { // bigBangController.breaker(); // } // state.addBreakRequestedListener(onBreak); var bigBangController = rawJsworld.bigBang( toplevelNode, initW, configs, {}, function(finalWorldValue) { // state.removeBreakRequestedListener(onBreak); restart(function(MACHINE) { finalizeClosureCall( MACHINE, finalWorldValue); }); }); }); }; ////////////////////////////////////////////////////////////////////// // Every world configuration function (on-tick, stop-when, ...) // produces a WorldConfigOption instance. var WorldConfigOption = function(name) { this.name = name; }; WorldConfigOption.prototype.configure = function(config) { throw new Error('unimplemented WorldConfigOption'); }; WorldConfigOption.prototype.toDomNode = function(params) { var span = document.createElement('span'); span.appendChild(document.createTextNode("(" + this.name + " ...)")); return span; }; WorldConfigOption.prototype.toWrittenString = function(cache) { return "(" + this.name + " ...)"; }; WorldConfigOption.prototype.toDisplayedString = function(cache) { return "(" + this.name + " ...)"; }; var isWorldConfigOption = plt.baselib.makeClassPredicate(WorldConfigOption); ////////////////////////////////////////////////////////////////////// // adaptWorldFunction: Racket-function -> World-CPS // Takes a racket function and converts it to the CPS-style function // that our world implementation expects. var adaptWorldFunction = function(worldFunction) { return function() { // Consumes any number of arguments. var success = arguments[arguments.length - 1]; plt.baselib.functions.internalCallDuringPause.apply( null, [MACHINE, worldFunction, function(v) { success(v); }, function(err) { // FIXME: do error trapping if (window.console && window.console.log) { window.console.log(err); } else { throw err; } }].concat([].slice.call(arguments, 0, arguments.length - 1))); }; }; ////////////////////////////////////////////////////////////////////// // OnTick: racket-function javascript-float -> handler var OnTick = function(handler, aDelay) { WorldConfigOption.call(this, 'on-tick'); this.handler = handler; this.delay = aDelay; }; OnTick.prototype = plt.baselib.heir(WorldConfigOption.prototype); OnTick.prototype.toRawHandler = function(MACHINE, toplevelNode) { var that = this; var worldFunction = adaptWorldFunction(that.handler); return rawJsworld.on_tick(this.delay, worldFunction); }; ////////////////////////////////////////////////////////////////////// var OnKey = function(handler) { WorldConfigOption.call(this, 'on-key'); this.handler = handler; } OnKey.prototype = plt.baselib.heir(WorldConfigOption.prototype); OnKey.prototype.toRawHandler = function(MACHINE, toplevelNode) { var that = this; var worldFunction = adaptWorldFunction(that.handler); return rawJsworld.on_key( function(w, e, success) { worldFunction(w, getKeyCodeName(e), success); }); }; 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 OnMouse = function(handler) { WorldConfigOption.call(this, 'on-mouse'); this.handler = handler; } OnMouse.prototype = plt.baselib.heir(WorldConfigOption.prototype); OnMouse.prototype.toRawHandler = function(MACHINE, toplevelNode) { var that = this; var worldFunction = adaptWorldFunction(that.handler); return rawJsworld.on_mouse( function(w, x, y, type, success) { worldFunction(w, x, y, type, success); }); }; var OutputConfig = function() {} OutputConfig.prototype = plt.baselib.heir(WorldConfigOption.prototype); var isOutputConfig = plt.baselib.makeClassPredicate(OutputConfig); // // ToDraw var ToDraw = function(handler) { WorldConfigOption.call(this, 'to-draw'); this.handler = handler; }; ToDraw.prototype = plt.baselib.heir(OutputConfig.prototype); ToDraw.prototype.toRawHandler = function(MACHINE, toplevelNode) { var that = this; var reusableCanvas; var reusableCanvasNode; var adaptedWorldFunction = adaptWorldFunction(this.handler); var worldFunction = function(world, success) { adaptedWorldFunction( world, function(v) { // fixme: once jsworld supports fail continuations, use them // to check the status of the scene object and make sure it's an // image. if (isImage(v) ) { var width = v.getWidth(); var height = v.getHeight(); if (! reusableCanvas) { reusableCanvas = imageLibrary.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 = rawJsworld.node_to_tree(reusableCanvas); } if (reusableCanvas.width !== width) { reusableCanvas.width = width; } if (reusableCanvas.height !== height) { reusableCanvas.height = height; } var ctx = reusableCanvas.getContext("2d"); v.render(ctx, 0, 0); success([toplevelNode, reusableCanvasNode]); } else { success([toplevelNode, rawJsworld.node_to_tree(plt.baselib.format.toDomNode(v, MACHINE.params['print-mode']))]); } }); }; var cssFunction = function(w, k) { if (reusableCanvas) { k([[reusableCanvas, ["padding", "0px"], ["width", reusableCanvas.width + "px"], ["height", reusableCanvas.height + "px"]]]); } else { k([]); } } return rawJsworld.on_draw(worldFunction, cssFunction); }; var DefaultDrawingOutput = function() { WorldConfigOption.call(this, 'to-draw'); }; DefaultDrawingOutput.prototype = plt.baselib.heir(WorldConfigOption.prototype); DefaultDrawingOutput.prototype.toRawHandler = function(MACHINE, toplevelNode) { var that = this; var worldFunction = function(world, success) { success([toplevelNode, rawJsworld.node_to_tree(plt.baselib.format.toDomNode(world, MACHINE.params['print-mode']))]); //k(rawJsworld.node_to_tree(plt.baselib.format.toDomNode(world))); }; var cssFunction = function(w, success) { success([]); } return rawJsworld.on_draw(worldFunction, cssFunction); }; ////////////////////////////////////////////////////////////////////// var StopWhen = function(handler) { WorldConfigOption.call(this, 'stop-when'); this.handler = handler; }; StopWhen.prototype = plt.baselib.heir(WorldConfigOption.prototype); StopWhen.prototype.toRawHandler = function(MACHINE, toplevelNode) { var that = this; var worldFunction = adaptWorldFunction(that.handler); return rawJsworld.stop_when(worldFunction); };