tick-tock is doing something
This commit is contained in:
parent
84c6df5f90
commit
c5d8933126
|
@ -7,7 +7,8 @@
|
|||
|
||||
(declare-implementation
|
||||
#:racket "racket-impl.rkt"
|
||||
#:javascript ("js-impl.js")
|
||||
#:javascript ("js-tree-cursor.js"
|
||||
"js-impl.js")
|
||||
#:provided-values (big-bang
|
||||
|
||||
;; initial view
|
||||
|
|
|
@ -16,45 +16,110 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
// See Functional Pearl: The Zipper, by G\'erard Huet
|
||||
// J. Functional Programming 7 (5): 549--554 Sepember 1997
|
||||
var TreeCursor = function() {
|
||||
this.parent;
|
||||
this
|
||||
var TreePath = function(parent, node, prevs, nexts) {
|
||||
this.parent = parent; // Parent can be the top (undefined), or a TreePath
|
||||
this.node = node;
|
||||
this.prevs = prevs;
|
||||
this.nexts = nexts;
|
||||
};
|
||||
|
||||
TreePath.prototype.down = function() {
|
||||
var children = node.children();
|
||||
return new TreePath(this, node[0], [], children.slice(1));
|
||||
};
|
||||
|
||||
TreePath.prototype.up = function() {
|
||||
var parent = this.parent;
|
||||
return new Tree
|
||||
};
|
||||
|
||||
TreePath.prototype.left = function() {
|
||||
};
|
||||
|
||||
TreePath.prototype.right = function() {
|
||||
};
|
||||
|
||||
TreePath.prototype.succ = function() {
|
||||
};
|
||||
|
||||
TreePath.prototype.pred = function() {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// For the moment, we only support selection by id.
|
||||
var idRegexp = new RegExp("^#");
|
||||
var selectorMatches = function(selector, node) {
|
||||
if (selector.match(idRegexp)) {
|
||||
if (node.nodeType === 1) {
|
||||
return node.getAttribute('id') === selector.substring(1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
var MockView = function(focused, pendingActions) {
|
||||
this.focused = focused;
|
||||
var MockView = function(cursor, pendingActions) {
|
||||
this.cursor = cursor;
|
||||
this.pendingActions = pendingActions;
|
||||
};
|
||||
|
||||
var isMockView = plt.baselib.makeClassPredicate(MockView);
|
||||
|
||||
MockView.prototype.act = function(actionForMock, actionForReal) {
|
||||
MockView.prototype.act = function(actionForCursor, actionForReal) {
|
||||
if (arguments.length !== 2) { throw new Error("act: insufficient arguments"); }
|
||||
|
||||
// FIXME: this is not enough. We need a way to do the action
|
||||
// on a copy of the mock. clone is insufficient: we need to
|
||||
// copy the whole tree, no?
|
||||
return new MockView(actionForMock(this.focused),
|
||||
return new MockView(actionForCursor(this.cursor),
|
||||
this.pendingActions.concat([actionForReal]));
|
||||
};
|
||||
|
||||
MockView.prototype.updateFocus = function(selector) {
|
||||
return this;
|
||||
selector = selector.toString();
|
||||
return this.act(
|
||||
function(cursor) {
|
||||
var c = cursor.top();
|
||||
while (true) {
|
||||
if (selectorMatches(selector, c.node)) {
|
||||
return c;
|
||||
}
|
||||
if (c.canSucc()) {
|
||||
c = c.succ();
|
||||
} else {
|
||||
throw new Error("unable to find " + selector);
|
||||
}
|
||||
}
|
||||
},
|
||||
function(view) {
|
||||
view.focus = view.top.find(selector);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
MockView.prototype.getText = function() {
|
||||
return "fill me in";
|
||||
return $(this.cursor.node).text();
|
||||
};
|
||||
|
||||
MockView.prototype.updateText = function(text) {
|
||||
return this;
|
||||
return this.act(
|
||||
function(cursor) {
|
||||
return cursor.replaceNode($(cursor.node).text(text).get(0));
|
||||
},
|
||||
function(view) {
|
||||
view.focus.text(text);
|
||||
})
|
||||
};
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -63,10 +128,10 @@
|
|||
|
||||
|
||||
// A View represents a representation of the DOM tree.
|
||||
var View = function(top, focused, eventHandlers, pendingActions, proxy) {
|
||||
var View = function(top, eventHandlers) {
|
||||
// top: dom node
|
||||
this.top = top;
|
||||
this.focused = focused;
|
||||
this.focus = top;
|
||||
this.eventHandlers = eventHandlers;
|
||||
};
|
||||
|
||||
|
@ -80,6 +145,7 @@
|
|||
// children and apply them here?
|
||||
if (this.top.find("body").length > 0) {
|
||||
top.append(this.top.find("body").children());
|
||||
this.top = top;
|
||||
} else {
|
||||
top.append(this.top);
|
||||
}
|
||||
|
@ -91,43 +157,14 @@
|
|||
return this.eventHandlers;
|
||||
};
|
||||
|
||||
View.prototype.getMock = function() {
|
||||
return new MockView(this.top.clone(true), []);
|
||||
View.prototype.getMockAndResetFocus = function() {
|
||||
this.focus = this.top;
|
||||
return new MockView(TreeCursor.domToCursor($(this.top).get(0)),
|
||||
[]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// View.prototype.updateFocus = function(selector) {
|
||||
// if (this.proxy) {
|
||||
// return new View(this.top, this.top.find(selector), this.eventHandlers, this.pendingActions, this.proxy);
|
||||
// } else {
|
||||
// return new View(this.top, this.top.find(selector), this.eventHandlers, this.pendingActions, this.proxy);
|
||||
// }
|
||||
// };
|
||||
|
||||
// View.prototype.text = function() {
|
||||
// if (this.proxy) {
|
||||
// return (this.proxy.text())
|
||||
// } else {
|
||||
// return (this.focused.text());
|
||||
// }
|
||||
// };
|
||||
|
||||
// View.prototype.updateText = function(s) {
|
||||
// if (this.proxy) {
|
||||
// this.proxy.text(s);
|
||||
// return new View(this.top,
|
||||
// this.focused,
|
||||
// this.eventHandlers,
|
||||
// this.pendingActions.concat([ function(v) { this.focused.text(s); }]),
|
||||
// this.proxy);
|
||||
// } else {
|
||||
// return (this.focused.text());
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var isView = plt.baselib.makeClassPredicate(View);
|
||||
|
@ -159,22 +196,15 @@
|
|||
} catch (exn) {
|
||||
return onFail(exn);
|
||||
}
|
||||
return onSuccess(new View(dom,
|
||||
dom,
|
||||
[],
|
||||
[],
|
||||
undefined));
|
||||
return onSuccess(new View(dom, []));
|
||||
|
||||
} else {
|
||||
try {
|
||||
dom = $(plt.baselib.format.toDomNode(x))
|
||||
} catch (exn) {
|
||||
return onFail(exn);
|
||||
}
|
||||
return onSuccess(new View(dom,
|
||||
dom,
|
||||
[],
|
||||
[],
|
||||
undefined));
|
||||
return onSuccess(new View(dom, []));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -388,12 +418,12 @@
|
|||
// bigBang.
|
||||
var bigBang = function(MACHINE, world, handlers) {
|
||||
var oldArgcount = MACHINE.argcount;
|
||||
|
||||
var running = true;
|
||||
var dispatchingEvents = false;
|
||||
|
||||
var top = $(plt.baselib.format.toDomNode(world));
|
||||
var view = (find(handlers, isInitialViewHandler) || { view : new View(top,
|
||||
top,
|
||||
[],
|
||||
[])}).view;
|
||||
var view = (find(handlers, isInitialViewHandler) || { view : new View(top, [])}).view;
|
||||
var stopWhen = (find(handlers, isStopWhenHandler) || { stopWhen: defaultStopWhen }).stopWhen;
|
||||
var toDraw = (find(handlers, isToDrawHandler) || {toDraw : defaultToDraw} ).toDraw;
|
||||
|
||||
|
@ -434,25 +464,25 @@
|
|||
var data;
|
||||
var racketWorldCallback;
|
||||
var mockView;
|
||||
|
||||
dispatchingEvents = true;
|
||||
if(! eventQueue.isEmpty() ) {
|
||||
// Set up the proxy object so we can do what appear to be functional
|
||||
// queries.
|
||||
mockView = view.getMock();
|
||||
mockView = view.getMockAndResetFocus();
|
||||
|
||||
nextEvent = eventQueue.dequeue();
|
||||
// FIXME: deal with event data here
|
||||
racketWorldCallback = nextEvent.handler.racketWorldCallback;
|
||||
racketWorldCallback(MACHINE,
|
||||
world,
|
||||
view,
|
||||
mockView,
|
||||
// data,
|
||||
function(newWorld) {
|
||||
world = newWorld;
|
||||
|
||||
stopWhen(MACHINE,
|
||||
world,
|
||||
view,
|
||||
mockView,
|
||||
function(shouldStop) {
|
||||
if (shouldStop) {
|
||||
onCleanRestart();
|
||||
|
@ -469,7 +499,21 @@
|
|||
onMessyRestart(err);
|
||||
});
|
||||
} else {
|
||||
// call redraw
|
||||
toDraw(MACHINE,
|
||||
world,
|
||||
view.getMockAndResetFocus(),
|
||||
function(newMockView) {
|
||||
var i;
|
||||
var actions = newMockView.pendingActions;
|
||||
for (i = 0; i < actions.length; i++) {
|
||||
actions[i](view);
|
||||
}
|
||||
dispatchingEvents = false;
|
||||
},
|
||||
function(err) {
|
||||
dispatchingEvents = false;
|
||||
onMessyRestart(err);
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -479,7 +523,9 @@
|
|||
if (! running) { return; }
|
||||
var args = [].slice.call(arguments, 0);
|
||||
eventQueue.queue(new EventQueueElement(handler, args));
|
||||
setTimeout(dispatchEventsInQueue, 0);
|
||||
if (! dispatchingEvents) {
|
||||
setTimeout(dispatchEventsInQueue, 0);
|
||||
}
|
||||
//
|
||||
// fixme: if we see too many events accumulating, throttle
|
||||
// the ones that are marked as throttleable.
|
||||
|
@ -498,19 +544,6 @@
|
|||
for (i = 0; i < eventHandlers.length; i++) {
|
||||
startEventHandler(eventHandlers[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// fixme: set up the event sources
|
||||
// fixme: set up the world updater
|
||||
// fixme: re-render the view on world changes.
|
||||
|
||||
|
||||
// Initialize event handlers to send to that channel.
|
||||
|
||||
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -532,6 +565,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
var checkReal = plt.baselib.check.checkReal;
|
||||
var checkString = plt.baselib.check.checkString;
|
||||
|
||||
var checkProcedure = plt.baselib.check.checkProcedure;
|
||||
|
||||
|
@ -543,7 +577,7 @@
|
|||
isWorldHandler,
|
||||
'world handler');
|
||||
|
||||
var checkView = plt.baselib.check.makeCheckArgumentType(
|
||||
var checkMockView = plt.baselib.check.makeCheckArgumentType(
|
||||
isMockView, 'view');
|
||||
|
||||
|
||||
|
@ -654,7 +688,7 @@
|
|||
'view-focus',
|
||||
2,
|
||||
function(MACHINE) {
|
||||
var view = checkView(MACHINE, 'view-focus', 0);
|
||||
var view = checkMockView(MACHINE, 'view-focus', 0);
|
||||
var selector = checkSelector(MACHINE, 'view-focus', 1);
|
||||
try {
|
||||
return view.updateFocus(selector);
|
||||
|
@ -662,8 +696,8 @@
|
|||
plt.baselib.exceptions.raise(
|
||||
MACHINE,
|
||||
new Error(plt.baselib.format.format(
|
||||
"unable to focus to ~s",
|
||||
[selector])));
|
||||
"unable to focus to ~s: ~s",
|
||||
[selector, e.message])));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -671,7 +705,7 @@
|
|||
'view-focus',
|
||||
1,
|
||||
function(MACHINE) {
|
||||
var view = checkView(MACHINE, 'view-focus', 0);
|
||||
var view = checkMockView(MACHINE, 'view-focus', 0);
|
||||
return view.getText();
|
||||
});
|
||||
|
||||
|
@ -680,8 +714,8 @@
|
|||
'update-view-text',
|
||||
2,
|
||||
function(MACHINE) {
|
||||
var view = checkView(MACHINE, 'update-view-text', 0);
|
||||
var text = checkString(MACHINE, 'update-view-text', 1);
|
||||
var view = checkMockView(MACHINE, 'update-view-text', 0);
|
||||
var text = plt.baselib.format.toDisplayedString(MACHINE.env[MACHINE.env.length - 2]);
|
||||
return view.updateText(text);
|
||||
});
|
||||
|
||||
|
|
308
web-world/js-tree-cursor.js
Normal file
308
web-world/js-tree-cursor.js
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*jslint vars: true, white: true, plusplus: true, maxerr: 50, indent: 4 */
|
||||
|
||||
|
||||
|
||||
// Offers functional views, traversals of the DOM and other tree-like structures.
|
||||
// See Functional Pearl: The Zipper, by G\'erard Huet
|
||||
// J. Functional Programming 7 (5): 549--554 Sepember 1997
|
||||
|
||||
|
||||
var TreeCursor = (function() {
|
||||
"use strict";
|
||||
|
||||
var TreeCursor = function(parent, node, prevs, nexts, openF, closeF, atomicF) {
|
||||
this.parent = parent; // Parent can be the top (undefined), or a TreeCursor
|
||||
this.node = node;
|
||||
this.prevs = prevs;
|
||||
this.nexts = nexts;
|
||||
|
||||
// openF: node -> (arrayof node)
|
||||
this.openF = openF;
|
||||
|
||||
// closeF: node (arrayof node) -> node
|
||||
// Given a node and its array of children, return a new node.
|
||||
this.closeF = closeF;
|
||||
|
||||
// atomicF: node -> boolean
|
||||
// Produces true if the node should be treated atomically.
|
||||
this.atomicF = atomicF;
|
||||
};
|
||||
|
||||
TreeCursor.prototype.canDown = function() {
|
||||
return (!(this.atomicF(this.node)) &&
|
||||
this.openF(this.node).length !== 0);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.down = function() {
|
||||
if (this.atomicF(this.node)) {
|
||||
throw new Error("down of atomic element");
|
||||
}
|
||||
var opened = this.openF(this.node);
|
||||
if (opened.length === 0) {
|
||||
throw new Error("down of empty");
|
||||
}
|
||||
return new TreeCursor(this,
|
||||
opened[0],
|
||||
[],
|
||||
opened.slice(1),
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
|
||||
TreeCursor.prototype.canUp = function() {
|
||||
return this.parent !== undefined;
|
||||
};
|
||||
|
||||
TreeCursor.prototype.up = function() {
|
||||
var parent = this.parent;
|
||||
return new TreeCursor(parent.parent,
|
||||
this.closeF(parent.node,
|
||||
this.prevs.concat([this.node]).concat(this.nexts)),
|
||||
parent.prevs,
|
||||
parent.nexts,
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.canLeft = function() { return this.prevs.length !== 0; };
|
||||
|
||||
TreeCursor.prototype.left = function() {
|
||||
if (this.prevs.length === 0) { throw new Error("left of first"); }
|
||||
return new TreeCursor(this.parent,
|
||||
this.prevs[this.prevs.length - 1],
|
||||
this.prevs.slice(0, this.prevs.length - 1),
|
||||
[this.node].concat(this.nexts),
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.canRight = function() { return this.nexts.length !== 0; };
|
||||
|
||||
TreeCursor.prototype.right = function() {
|
||||
if (this.nexts.length === 0) { throw new Error("right of last"); }
|
||||
return new TreeCursor(this.parent,
|
||||
this.nexts[0],
|
||||
this.prevs.concat([this.node]),
|
||||
this.nexts.slice(1),
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.succ = function() {
|
||||
var n;
|
||||
if (this.canDown()) {
|
||||
return this.down();
|
||||
} else if (this.canRight()) {
|
||||
return this.right();
|
||||
} else {
|
||||
n = this;
|
||||
while (true) {
|
||||
n = n.up();
|
||||
if (n.canRight()) {
|
||||
return n.right();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TreeCursor.prototype.pred = function() {
|
||||
var n;
|
||||
if (this.canLeft()) {
|
||||
n = this.left();
|
||||
while (n.canDown()) {
|
||||
n = n.down();
|
||||
while (n.canRight()) {
|
||||
n = n.right();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
} else {
|
||||
return this.up();
|
||||
}
|
||||
};
|
||||
|
||||
TreeCursor.prototype.canPred = function() {
|
||||
return this.canLeft() || this.canUp();
|
||||
};
|
||||
|
||||
TreeCursor.prototype.canSucc = function() {
|
||||
var n;
|
||||
if (this.canDown()) {
|
||||
return true;
|
||||
} else if (this.canRight()) {
|
||||
return true;
|
||||
} else {
|
||||
n = this;
|
||||
while (true) {
|
||||
if (! n.canUp()) { return false; }
|
||||
n = n.up();
|
||||
if (n.canRight()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TreeCursor.prototype.top = function() {
|
||||
var n = this;
|
||||
while (n.canUp()) { n = n.up(); }
|
||||
return n;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
TreeCursor.prototype.replaceNode = function(n) {
|
||||
return new TreeCursor(this.parent,
|
||||
n,
|
||||
this.prevs,
|
||||
this.nexts,
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.insertRight = function(n) {
|
||||
return new TreeCursor(this.parent,
|
||||
n,
|
||||
this.prevs.concat(this.node),
|
||||
this.nexts,
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.insertLeft = function(n) {
|
||||
return new TreeCursor(this.parent,
|
||||
n,
|
||||
this.prevs,
|
||||
[this.node].concat(this.nexts),
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.insertDown = function(n) {
|
||||
if (this.atomicF(this.node)) {
|
||||
throw new Error("down of atomic element");
|
||||
}
|
||||
return new TreeCursor(this,
|
||||
n,
|
||||
[],
|
||||
this.openF(this.node),
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
};
|
||||
|
||||
TreeCursor.prototype.deleteNode = function() {
|
||||
var parent;
|
||||
if (this.nexts.length !== 0) {
|
||||
return new TreeCursor(this.parent,
|
||||
this.nexts[0],
|
||||
this.prevs,
|
||||
this.nexts.slice(1),
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
} else if (this.prevs.length !== 0) {
|
||||
return new TreeCursor(this.parent,
|
||||
this.prevs[this.prevs.length - 1],
|
||||
this.prevs.slice(0, this.prevs.length - 1),
|
||||
this.nexts,
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
} else {
|
||||
parent = this.parent;
|
||||
return new TreeCursor(parent.parent,
|
||||
this.closeF(parent.node, []),
|
||||
parent.prevs,
|
||||
parent.nexts,
|
||||
this.openF,
|
||||
this.closeF,
|
||||
this.atomicF);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
TreeCursor.adaptTreeCursor = function(node, openF, closeF, atomicF) {
|
||||
return new TreeCursor(undefined,
|
||||
node,
|
||||
[],
|
||||
[],
|
||||
openF,
|
||||
closeF,
|
||||
atomicF);
|
||||
};
|
||||
|
||||
|
||||
TreeCursor.arrayToCursor = function(anArray) {
|
||||
var arrayOpenF = function(n) {
|
||||
if (n instanceof Array) {
|
||||
return n;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
var arrayCloseF = function(n, children) {
|
||||
if (n instanceof Array) {
|
||||
return children;
|
||||
} else {
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
var arrayAtomicF = function(n) {
|
||||
return !(n instanceof Array);
|
||||
};
|
||||
return TreeCursor.adaptTreeCursor(anArray,
|
||||
arrayOpenF,
|
||||
arrayCloseF,
|
||||
arrayAtomicF);
|
||||
};
|
||||
|
||||
|
||||
TreeCursor.domToCursor = function(dom) {
|
||||
var domOpenF =
|
||||
// To go down, just take the children.
|
||||
function(n) {
|
||||
return [].slice.call(n.childNodes, 0);
|
||||
};
|
||||
var domCloseF =
|
||||
// To go back up, take the node, do a shallow cloning, and replace the children.
|
||||
function(node, children) {
|
||||
var i;
|
||||
var newNode = node.cloneNode(false);
|
||||
for (i = 0; i < children.length; i++) {
|
||||
newNode.appendChild(children[i].cloneNode(true));
|
||||
}
|
||||
return newNode;
|
||||
};
|
||||
var domAtomicF =
|
||||
function(node) {
|
||||
return node.nodeType !== 1;
|
||||
};
|
||||
return TreeCursor.adaptTreeCursor(dom.cloneNode(true),
|
||||
domOpenF,
|
||||
domCloseF,
|
||||
domAtomicF);
|
||||
};
|
||||
|
||||
|
||||
return TreeCursor;
|
||||
}());
|
Loading…
Reference in New Issue
Block a user