375 lines
10 KiB
JavaScript
375 lines
10 KiB
JavaScript
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 = $('<span/>').css('padding', '0px').get(0);
|
|
MACHINE.params.currentOutputPort.writeDomNode(MACHINE, outerToplevelNode);
|
|
var toplevelNode = $('<span/>').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);
|
|
};
|