diff --git a/world/raw-jsworld.js b/world/raw-jsworld.js index 97aa7a0..3c6ffc7 100644 --- a/world/raw-jsworld.js +++ b/world/raw-jsworld.js @@ -8,6 +8,8 @@ var rawJsworld = {}; (function() { + 'use strict'; + /* Type signature notation * CPS(a b ... -> c) is used to denote * a b ... (c -> void) -> void @@ -27,22 +29,22 @@ var rawJsworld = {}; // Iterates through an array and applies f to each element using CPS // If an error is thrown, it catches the error and calls f_error on it var forEachK = function(a, f, f_error, k) { - var forEachHelp = function(i) { - if( i >= a.length ) { - if (k) { - return k(); - } else { - return; - } - } + var forEachHelp = function(i) { + if( i >= a.length ) { + if (k) { + return k(); + } else { + return; + } + } - try { - return f(a[i], function() { return forEachHelp(i+1); }); - } catch (e) { - f_error(e); - } - }; - return forEachHelp(0); + try { + return f(a[i], function() { return forEachHelp(i+1); }); + } catch (e) { + f_error(e); + } + }; + return forEachHelp(0); }; @@ -66,38 +68,47 @@ var rawJsworld = {}; + function clear_running_state() { + var i; + world = new InitialWorld(); + worldListeners = []; + + for (i = 0; i < eventDetachers.length; i++) { + eventDetachers[i](); + } + eventDetachers = []; + changingWorld = false; + } + + + // Close all world computations. Jsworld.shutdown = function() { - while(runningBigBangs.length > 0) { - var currentRecord = runningBigBangs.pop(); - if (currentRecord) { currentRecord.pause(); } - } - clear_running_state(); - } + while(runningBigBangs.length > 0) { + var currentRecord = runningBigBangs.pop(); + if (currentRecord) { currentRecord.pause(); } + } + clear_running_state(); + }; function add_world_listener(listener) { - worldListeners.push(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; + var i, index = -1; + for (i = 0; i < worldListeners.length; i++) { + if (worldListeners[i] === listener) { + index = i; + break; + } + } + if (index !== -1) { + worldListeners.splice(index, 1); + } } @@ -105,75 +116,79 @@ var rawJsworld = {}; var DELAY_BEFORE_RETRY = 10; + var WrappedWorldWithEffects; + // 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; - } + // 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; + changingWorld = true; + var originalWorld = world; - var changeWorldHelp = function() { - if (world instanceof WrappedWorldWithEffects) { - var effects = world.getEffects(); - 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() { - forEachK(worldListeners, - function(listener, k2) { - listener(world, originalWorld, k2); - }, - function(e) { - changingWorld = false; - world = originalWorld; - throw e; }, - function() { - changingWorld = false; - k(); - }); - }; + var changeWorldHelp, changeWorldHelp2; + changeWorldHelp = function() { + if (world instanceof WrappedWorldWithEffects) { + var effects = world.getEffects(); + forEachK(effects, + function(anEffect, k2) { + anEffect.invokeEffect(change_world, k2); + }, + function (e) { + changingWorld = false; + throw e; + }, + function() { + world = world.getWorld(); + changeWorldHelp2(); + }); + } else { + changeWorldHelp2(); + } + }; - try { - updater(world, function(newWorld) { - world = newWorld; - changeWorldHelp(); - }); - } catch(e) { - changingWorld = false; - world = originalWorld; + changeWorldHelp2 = function() { + forEachK(worldListeners, + function(listener, k2) { + listener(world, originalWorld, k2); + }, + function(e) { + changingWorld = false; + world = originalWorld; + throw e; }, + function() { + changingWorld = false; + k(); + }); + }; - if (typeof(console) !== 'undefined' && console.log && e.stack) { - console.log(e.stack); - } - throw e; - } - } + try { + updater(world, function(newWorld) { + world = newWorld; + changeWorldHelp(); + }); + } catch(e) { + changingWorld = false; + world = originalWorld; + + if (typeof(window.console) !== 'undefined' && window.console.log && e.stack) { + window.console.log(e.stack); + } + throw e; + } + }; Jsworld.change_world = change_world; @@ -182,18 +197,18 @@ var rawJsworld = {}; // // STUFF THAT SHOULD REALLY BE IN ECMASCRIPT // - Number.prototype.NaN0=function(){return isNaN(this)?0:this;} + 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}; + var left = 0; + var top = 0; + while (e.offsetParent){ + left += e.offsetLeft + (e.currentStyle?(Number(e.currentStyle.borderLeftWidth)).NaN0():0); + top += e.offsetTop + (e.currentStyle?(Number(e.currentStyle.borderTopWidth)).NaN0():0); + e = e.offsetParent; + } + left += e.offsetLeft + (e.currentStyle?(Number(e.currentStyle.borderLeftWidth)).NaN0():0); + top += e.offsetTop + (e.currentStyle?(Number(e.currentStyle.borderTopWidth)).NaN0():0); + return {x:left, y:top}; } Jsworld.getPosition = getPosition; @@ -204,151 +219,158 @@ var rawJsworld = {}; var map = function(a1, f) { - var b = new Array(a1.length); - for (var i = 0; i < a1.length; i++) { - b[i] = f(a1[i]); - } - return b; - } + var b = new Array(a1.length), i; + for (i = 0; i < a1.length; i++) { + b[i] = f(a1[i]); + } + return b; + }; Jsworld.map = map; var concat_map = function(a, f) { - var b = []; - for (var i = 0; i < a.length; i++) { - b = b.concat(f(a[i])); - } - return b; - } + var b = [], i; + for (i = 0; i < a.length; i++) { + b = b.concat(f(a[i])); + } + return b; + }; var mapi = function(a, f) { - var b = new Array(a.length); - for (var i = 0; i < a.length; i++) { - b[i] = f(a[i], i); - } - return b; - } + var b = new Array(a.length), i; + for (i = 0; i < a.length; i++) { + b[i] = f(a[i], i); + } + return b; + }; Jsworld.mapi = mapi; var fold = function(a, x, f) { - for (var i = 0; i < a.length; i++) { - x = f(a[i], x); - } - return x; - } + var i; + for (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; - } + 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; + }; 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; + 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); + return [value].concat(array); } Jsworld.cons = cons; function append(array1, array2){ - return array1.concat(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; + 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; + 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; + 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; + 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; + 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; + 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; + 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]; + return a[0]; } Jsworld.head = head; function tail(a){ - return a.slice(1, a.length); + return a.slice(1, a.length); } Jsworld.tail = tail; @@ -414,11 +436,11 @@ var rawJsworld = {}; // 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; + 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; @@ -426,329 +448,329 @@ var rawJsworld = {}; // nodes(tree(N)) = nodes(N) function nodes(tree) { - var ret; - - if (tree.node.jsworldOpaque == true) { - return [tree.node]; - } + 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; + 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 = []; + 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; + 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); - } + 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; - } - }); + 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; - } - } - } - } + // 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(); - } - } - }); + // 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); + 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; + 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); + return (node1 && node2 && node1 === node2); } // camelCase: string -> string function camelCase(name) { - return name.replace(/\-(.)/g, function(m, l){return l.toUpperCase()}); + 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(" "); - } + 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); - } + 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); + // 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 = ""; + // 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); - } - } + 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); + 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. + // 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)); - } - } - */ + 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)); + } + } + */ - redraw_func( + redraw_func( world, - function(newRedraw) { + function(newRedraw) { - redraw_css_func( + redraw_css_func( world, - function(newRedrawCss) { - var t = sexp2tree(newRedraw); - var ns = nodes(t); + function(newRedrawCss) { + var t = sexp2tree(newRedraw); + var ns = nodes(t); - // Try to save the current selection and preserve it across - // dom updates. + // Try to save the current selection and preserve it across + // dom updates. - // 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)); + // 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)); - k2(); - }) - }) + k2(); + }) + }) - }, k); - } + }, 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(); }); - } + 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; + 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(); - } - } + // 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; + return currentFocusedNode != undefined; } function getCurrentFocusedSelection() { - return new FocusedSelection(); + return new FocusedSelection(); } @@ -756,25 +778,25 @@ var rawJsworld = {}; ////////////////////////////////////////////////////////////////////// function BigBangRecord(top, world, handlerCreators, handlers, attribs) { - this.top = top; - this.world = world; - this.handlers = handlers; - this.handlerCreators = handlerCreators; - this.attribs = attribs; + this.top = top; + this.world = world; + this.handlers = handlers; + this.handlerCreators = handlerCreators; + this.attribs = attribs; } BigBangRecord.prototype.restart = function() { - bigBang(this.top, this.world, this.handlerCreators, this.attribs); + bigBang(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); - } - } + 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); + } + } }; ////////////////////////////////////////////////////////////////////// @@ -787,64 +809,64 @@ var rawJsworld = {}; // handlerCreators: (Arrayof (-> handler)) // k: any -> void function bigBang(top, init_world, handlerCreators, attribs, succ) { - // clear_running_state(); + // 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(); - } + // 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); + // 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); - } - } - var watchForTermination = function(w, oldW, k2) { - stopWhen.test(w, - function(stop) { - if (stop) { - Jsworld.shutdown(); - succ(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); + // 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); + } + } + var watchForTermination = function(w, oldW, k2) { + stopWhen.test(w, + function(stop) { + if (stop) { + Jsworld.shutdown(); + succ(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); + // Finally, begin the big-bang. + copy_attribs(top, attribs); + change_world(function(w, k2) { k2(init_world); }, doNothing); } @@ -856,63 +878,63 @@ var rawJsworld = {}; // on_tick: number CPS(world -> world) -> handler var on_tick = function(delay, tick) { - return function() { - var scheduleTick, ticker; + return function() { + var scheduleTick, ticker; - (new Date()).valueOf() + (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); - }, + 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; - }; + 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) { + return function() { + var wrappedPress = function(e) { + preventDefault(e); + stopPropagation(e); + change_world(function(w, k) { press(w, e, k); }, doNothing); + }; + return { + onRegister: function(top) { //http://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribue $(top).attr('tabindex', 1); $(top).focus(); attachEvent(top, 'keydown', wrappedPress); }, - onUnregister: function(top) { + onUnregister: function(top) { detachEvent(top, 'keydown', wrappedPress); } - }; - } + }; + } } Jsworld.on_key = on_key; @@ -922,12 +944,12 @@ var rawJsworld = {}; // http://www.quirksmode.org/js/events_mouse.html // http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element function on_mouse(mouse) { - return function() { + return function() { var isButtonDown = false; - var makeWrapped = function(type) { + var makeWrapped = function(type) { return function(e) { - preventDefault(e); - stopPropagation(e); + preventDefault(e); + stopPropagation(e); var x = e.pageX, y = e.pageY; var currentElement = e.target; do { @@ -941,15 +963,15 @@ var rawJsworld = {}; isButtonDown = false; } if (type === 'move' && isButtonDown) { - change_world(function(w, k) { + change_world(function(w, k) { mouse(w, x, y, 'drag', k); }, doNothing); } else { - change_world(function(w, k) { + change_world(function(w, k) { mouse(w, x, y, type, k); }, doNothing); } - }; + }; }; var wrappedDown = makeWrapped('button-down'); var wrappedUp = makeWrapped('button-up'); @@ -957,23 +979,23 @@ var rawJsworld = {}; var wrappedMove = makeWrapped('move'); var wrappedEnter = makeWrapped('enter'); var wrappedLeave = makeWrapped('leave'); - return { - onRegister: function(top) { + return { + onRegister: function(top) { attachEvent(top, 'mousedown', wrappedDown); attachEvent(top, 'mouseup', wrappedUp); attachEvent(top, 'mousemove', wrappedMove); attachEvent(top, 'mouseenter', wrappedEnter); attachEvent(top, 'mouseleave', wrappedLeave); }, - onUnregister: function(top) { + onUnregister: function(top) { detachEvent(top, 'mousedown', wrappedDown); detachEvent(top, 'mouseup', wrappedUp); detachEvent(top, 'mousemove', wrappedMove); detachEvent(top, 'mouseenter', wrappedEnter); detachEvent(top, 'mouseleave', wrappedLeave); } - }; - } + }; + } } Jsworld.on_mouse = on_mouse; @@ -985,62 +1007,62 @@ var rawJsworld = {}; // 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); - }); - } + 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); - }, + 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; - }; + 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; + 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); - }; + 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)} - }; - }; + 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; @@ -1050,23 +1072,23 @@ var rawJsworld = {}; // 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); - } + 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); - } + if (node.addEventListener) { + // Mozilla + node.removeEventListener(eventName, fn, false); + } else { + // IE + node.detachEvent('on' + eventName, fn, false); + } } // @@ -1076,34 +1098,34 @@ var rawJsworld = {}; // 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); }); + 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); - }; + 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); }); + 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; + attachEvent(node, "focus", function(e) { + currentFocusedNode = node; }); + attachEvent(node, "blur", function(e) { + currentFocusedNode = undefined; + }); + return node; } @@ -1116,89 +1138,89 @@ var rawJsworld = {}; function sexp2tree(sexp) { - if(sexp.length == undefined) return { node: sexp, children: [] }; - else return { node: sexp[0], children: map(sexp.slice(1), sexp2tree) }; + 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) }; + 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 }]; - } + 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); + return concat_map(sexp, sexp2css_node); } function isTextNode(n) { - return (n.nodeType == Node.TEXT_NODE); + return (n.nodeType == Node.TEXT_NODE); }; function isElementNode(n) { - return (n.nodeType == Node.ELEMENT_NODE); + return (n.nodeType == Node.ELEMENT_NODE); }; var throwDomError = function(thing, topThing) { - throw new JsworldDomError( - plt.baselib.format.format( - "Expected a non-empty array, received ~s within ~s", - [thing, topThing]), - thing); + throw new JsworldDomError( + plt.baselib.format.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 (! thing instanceof Array) { - throwDomError(thing, topThing); - } - if (thing.length == 0) { - throwDomError(thing, topThing); - } + 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(plt.baselib.format.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 { + // Check that the first element is a Text or an element. + if (isTextNode(thing[0])) { + if (thing.length > 1) { + throw new JsworldDomError(plt.baselib.format.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 { console.log(thing[0]); - throw new JsworldDomError( - plt.baselib.format.format( - "expected a Text or an Element, received ~s within ~s", - [thing, topThing]), - thing[0]); - } + throw new JsworldDomError( + plt.baselib.format.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; + this.msg = msg; + this.elt = elt; } JsworldDomError.prototype.toString = function() { - return "JsworldDomError: " + this.msg; + return "JsworldDomError: " + this.msg; } @@ -1211,17 +1233,17 @@ var rawJsworld = {}; 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; + 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; } @@ -1230,22 +1252,22 @@ var rawJsworld = {}; // function p(attribs) { - return addFocusTracking(copy_attribs(document.createElement('p'), attribs)); + return addFocusTracking(copy_attribs(document.createElement('p'), attribs)); } Jsworld.p = p; function div(attribs) { - return addFocusTracking(copy_attribs(document.createElement('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)); + 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; @@ -1253,47 +1275,47 @@ var rawJsworld = {}; var preventDefault = function(event) { - if (event.preventDefault) { - event.preventDefault(); - } else { - event.returnValue = false; - } + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } } var stopPropagation = function(event) { - if (event.stopPropagation) { - event.stopPropagation(); - } else { - event.cancelBubble = true; - } + if (event.stopPropagation) { + event.stopPropagation(); + } else { + event.cancelBubble = true; + } } var stopClickPropagation = function(node) { - attachEvent(node, "click", - function(e) { - stopPropagation(e); - }); - return 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 - }; + 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); - } + if (dispatchTable[aType]) { + return (dispatchTable[aType])(aType, updateF, attribs); + } + else { + throw new Error("js-input: does not currently support type " + aType); + } } Jsworld.input = input; @@ -1301,58 +1323,58 @@ var rawJsworld = {}; var text_input = function(type, updateF, attribs) { - var n = document.createElement('input'); - n.type = type; + 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); - } + 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, "keydown", onEvent); - eventDetachers.push(function() { - detachEvent(n, "keydown", onEvent); }); + attachEvent(n, "keydown", onEvent); + eventDetachers.push(function() { + detachEvent(n, "keydown", onEvent); }); - attachEvent(n, "change", onEvent); - eventDetachers.push(function() { - detachEvent(n, "change", onEvent); }); + attachEvent(n, "change", onEvent); + eventDetachers.push(function() { + detachEvent(n, "change", onEvent); }); - return stopClickPropagation( - addFocusTracking(copy_attribs(n, attribs))); + 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); - }); + 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); + 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)); + var n = document.createElement('button'); + add_ev(n, 'click', function(w, e, k) { updateF(w, n.value, k); }); + return addFocusTracking(copy_attribs(n, attribs)); }; @@ -1360,61 +1382,61 @@ var rawJsworld = {}; function text(s, attribs) { - var result = document.createElement("div"); - result.appendChild(document.createTextNode(String(s))); - result.jsworldOpaque = true; - return result; + 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; + 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"); + var node = document.createElement("option"); node.text = attribs.value; - node.value = attribs.value; - return node; + node.value = attribs.value; + return node; } function textarea(attribs){ - return addFocusTracking(copy_attribs(document.createElement('textarea'), attribs)); + return addFocusTracking(copy_attribs(document.createElement('textarea'), attribs)); } Jsworld.textarea = textarea; function h1(attribs){ - return addFocusTracking(copy_attribs(document.createElement('h1'), attribs)); + return addFocusTracking(copy_attribs(document.createElement('h1'), attribs)); } Jsworld.h1 = h1; function canvas(attribs){ - return addFocusTracking(copy_attribs(document.createElement('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)); + 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)); + return addFocusTracking(copy_attribs(node, attribs)); } Jsworld.raw_node = raw_node; @@ -1427,38 +1449,37 @@ var rawJsworld = {}; // 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 = 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; + return this.w; }; WrappedWorldWithEffects.prototype.getEffects = function() { - return this.e; + return this.e; }; ////////////////////////////////////////////////////////////////////// Jsworld.with_effect = function(w, e) { - return new WrappedWorldWithEffects(w, [e]); + return new WrappedWorldWithEffects(w, [e]); }; Jsworld.with_multiple_effects = function(w, effects) { - return new WrappedWorldWithEffects(w, effects); + return new WrappedWorldWithEffects(w, effects); }; Jsworld.has_effects = function(w) { - return w instanceof WrappedWorldWithEffects; + return w instanceof WrappedWorldWithEffects; }; diff --git a/world/scratch/jsworld/define-effect.rkt b/world/scratch/jsworld/define-effect.rkt deleted file mode 100644 index 0b4e8f9..0000000 --- a/world/scratch/jsworld/define-effect.rkt +++ /dev/null @@ -1,82 +0,0 @@ -#lang s-exp "../lang/base.rkt" - -(require "jsworld.rkt") - -(require (for-syntax racket/base)) - -(provide define-effect) - -(define-syntax (define-effect stx) - (syntax-case stx () - - [(_ name (field ...) #:impl impl) - (identifier? #'name) - (syntax/loc stx - (define-effect (name #f) (field ...) #:impl impl))] - - [(_ (name supertype) (field ...) #:impl impl) - (with-syntax ([field-count - (length (syntax->list #'(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 deleted file mode 100644 index 748ee1e..0000000 --- a/world/scratch/jsworld/jsworld.js +++ /dev/null @@ -1,1396 +0,0 @@ - -/************************ - *** 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 deleted file mode 100644 index e706821..0000000 --- a/world/scratch/jsworld/jsworld.rkt +++ /dev/null @@ -1,50 +0,0 @@ -#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 deleted file mode 100644 index 17a0cc8..0000000 --- a/world/scratch/jsworld/private/jsworld.js +++ /dev/null @@ -1,777 +0,0 @@ -// 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 deleted file mode 100644 index f4d315d..0000000 --- a/world/scratch/jsworld/private/jsworld/jsworld.js +++ /dev/null @@ -1,1488 +0,0 @@ -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/kernel.js b/world/scratch/world/kernel.js deleted file mode 100644 index c0b51f9..0000000 --- a/world/scratch/world/kernel.js +++ /dev/null @@ -1,1938 +0,0 @@ -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 deleted file mode 100644 index f315428..0000000 --- a/world/scratch/world/kernel.rkt +++ /dev/null @@ -1,3 +0,0 @@ -#lang s-exp "../lang/js-impl/js-impl.rkt" - -(require-js "kernel.js")