tick-tock is doing something
This commit is contained in:
parent
84c6df5f90
commit
c5d8933126
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
(declare-implementation
|
(declare-implementation
|
||||||
#:racket "racket-impl.rkt"
|
#:racket "racket-impl.rkt"
|
||||||
#:javascript ("js-impl.js")
|
#:javascript ("js-tree-cursor.js"
|
||||||
|
"js-impl.js")
|
||||||
#:provided-values (big-bang
|
#:provided-values (big-bang
|
||||||
|
|
||||||
;; initial view
|
;; initial view
|
||||||
|
|
|
@ -16,45 +16,110 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// See Functional Pearl: The Zipper, by G\'erard Huet
|
// See Functional Pearl: The Zipper, by G\'erard Huet
|
||||||
// J. Functional Programming 7 (5): 549--554 Sepember 1997
|
// J. Functional Programming 7 (5): 549--554 Sepember 1997
|
||||||
var TreeCursor = function() {
|
var TreePath = function(parent, node, prevs, nexts) {
|
||||||
this.parent;
|
this.parent = parent; // Parent can be the top (undefined), or a TreePath
|
||||||
this
|
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) {
|
var MockView = function(cursor, pendingActions) {
|
||||||
this.focused = focused;
|
this.cursor = cursor;
|
||||||
this.pendingActions = pendingActions;
|
this.pendingActions = pendingActions;
|
||||||
};
|
};
|
||||||
|
|
||||||
var isMockView = plt.baselib.makeClassPredicate(MockView);
|
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"); }
|
if (arguments.length !== 2) { throw new Error("act: insufficient arguments"); }
|
||||||
|
|
||||||
// FIXME: this is not enough. We need a way to do the action
|
// 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
|
// on a copy of the mock. clone is insufficient: we need to
|
||||||
// copy the whole tree, no?
|
// copy the whole tree, no?
|
||||||
return new MockView(actionForMock(this.focused),
|
return new MockView(actionForCursor(this.cursor),
|
||||||
this.pendingActions.concat([actionForReal]));
|
this.pendingActions.concat([actionForReal]));
|
||||||
};
|
};
|
||||||
|
|
||||||
MockView.prototype.updateFocus = function(selector) {
|
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() {
|
MockView.prototype.getText = function() {
|
||||||
return "fill me in";
|
return $(this.cursor.node).text();
|
||||||
};
|
};
|
||||||
|
|
||||||
MockView.prototype.updateText = function(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.
|
// 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
|
// top: dom node
|
||||||
this.top = top;
|
this.top = top;
|
||||||
this.focused = focused;
|
this.focus = top;
|
||||||
this.eventHandlers = eventHandlers;
|
this.eventHandlers = eventHandlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,6 +145,7 @@
|
||||||
// children and apply them here?
|
// children and apply them here?
|
||||||
if (this.top.find("body").length > 0) {
|
if (this.top.find("body").length > 0) {
|
||||||
top.append(this.top.find("body").children());
|
top.append(this.top.find("body").children());
|
||||||
|
this.top = top;
|
||||||
} else {
|
} else {
|
||||||
top.append(this.top);
|
top.append(this.top);
|
||||||
}
|
}
|
||||||
|
@ -91,43 +157,14 @@
|
||||||
return this.eventHandlers;
|
return this.eventHandlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.getMock = function() {
|
View.prototype.getMockAndResetFocus = function() {
|
||||||
return new MockView(this.top.clone(true), []);
|
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);
|
var isView = plt.baselib.makeClassPredicate(View);
|
||||||
|
@ -159,22 +196,15 @@
|
||||||
} catch (exn) {
|
} catch (exn) {
|
||||||
return onFail(exn);
|
return onFail(exn);
|
||||||
}
|
}
|
||||||
return onSuccess(new View(dom,
|
return onSuccess(new View(dom, []));
|
||||||
dom,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
undefined));
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
dom = $(plt.baselib.format.toDomNode(x))
|
dom = $(plt.baselib.format.toDomNode(x))
|
||||||
} catch (exn) {
|
} catch (exn) {
|
||||||
return onFail(exn);
|
return onFail(exn);
|
||||||
}
|
}
|
||||||
return onSuccess(new View(dom,
|
return onSuccess(new View(dom, []));
|
||||||
dom,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
undefined));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -388,12 +418,12 @@
|
||||||
// bigBang.
|
// bigBang.
|
||||||
var bigBang = function(MACHINE, world, handlers) {
|
var bigBang = function(MACHINE, world, handlers) {
|
||||||
var oldArgcount = MACHINE.argcount;
|
var oldArgcount = MACHINE.argcount;
|
||||||
|
|
||||||
var running = true;
|
var running = true;
|
||||||
|
var dispatchingEvents = false;
|
||||||
|
|
||||||
var top = $(plt.baselib.format.toDomNode(world));
|
var top = $(plt.baselib.format.toDomNode(world));
|
||||||
var view = (find(handlers, isInitialViewHandler) || { view : new View(top,
|
var view = (find(handlers, isInitialViewHandler) || { view : new View(top, [])}).view;
|
||||||
top,
|
|
||||||
[],
|
|
||||||
[])}).view;
|
|
||||||
var stopWhen = (find(handlers, isStopWhenHandler) || { stopWhen: defaultStopWhen }).stopWhen;
|
var stopWhen = (find(handlers, isStopWhenHandler) || { stopWhen: defaultStopWhen }).stopWhen;
|
||||||
var toDraw = (find(handlers, isToDrawHandler) || {toDraw : defaultToDraw} ).toDraw;
|
var toDraw = (find(handlers, isToDrawHandler) || {toDraw : defaultToDraw} ).toDraw;
|
||||||
|
|
||||||
|
@ -434,25 +464,25 @@
|
||||||
var data;
|
var data;
|
||||||
var racketWorldCallback;
|
var racketWorldCallback;
|
||||||
var mockView;
|
var mockView;
|
||||||
|
dispatchingEvents = true;
|
||||||
if(! eventQueue.isEmpty() ) {
|
if(! eventQueue.isEmpty() ) {
|
||||||
// Set up the proxy object so we can do what appear to be functional
|
// Set up the proxy object so we can do what appear to be functional
|
||||||
// queries.
|
// queries.
|
||||||
mockView = view.getMock();
|
mockView = view.getMockAndResetFocus();
|
||||||
|
|
||||||
nextEvent = eventQueue.dequeue();
|
nextEvent = eventQueue.dequeue();
|
||||||
// FIXME: deal with event data here
|
// FIXME: deal with event data here
|
||||||
racketWorldCallback = nextEvent.handler.racketWorldCallback;
|
racketWorldCallback = nextEvent.handler.racketWorldCallback;
|
||||||
racketWorldCallback(MACHINE,
|
racketWorldCallback(MACHINE,
|
||||||
world,
|
world,
|
||||||
view,
|
mockView,
|
||||||
// data,
|
// data,
|
||||||
function(newWorld) {
|
function(newWorld) {
|
||||||
world = newWorld;
|
world = newWorld;
|
||||||
|
|
||||||
stopWhen(MACHINE,
|
stopWhen(MACHINE,
|
||||||
world,
|
world,
|
||||||
view,
|
mockView,
|
||||||
function(shouldStop) {
|
function(shouldStop) {
|
||||||
if (shouldStop) {
|
if (shouldStop) {
|
||||||
onCleanRestart();
|
onCleanRestart();
|
||||||
|
@ -469,7 +499,21 @@
|
||||||
onMessyRestart(err);
|
onMessyRestart(err);
|
||||||
});
|
});
|
||||||
} else {
|
} 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; }
|
if (! running) { return; }
|
||||||
var args = [].slice.call(arguments, 0);
|
var args = [].slice.call(arguments, 0);
|
||||||
eventQueue.queue(new EventQueueElement(handler, args));
|
eventQueue.queue(new EventQueueElement(handler, args));
|
||||||
|
if (! dispatchingEvents) {
|
||||||
setTimeout(dispatchEventsInQueue, 0);
|
setTimeout(dispatchEventsInQueue, 0);
|
||||||
|
}
|
||||||
//
|
//
|
||||||
// fixme: if we see too many events accumulating, throttle
|
// fixme: if we see too many events accumulating, throttle
|
||||||
// the ones that are marked as throttleable.
|
// the ones that are marked as throttleable.
|
||||||
|
@ -498,19 +544,6 @@
|
||||||
for (i = 0; i < eventHandlers.length; i++) {
|
for (i = 0; i < eventHandlers.length; i++) {
|
||||||
startEventHandler(eventHandlers[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 checkReal = plt.baselib.check.checkReal;
|
||||||
|
var checkString = plt.baselib.check.checkString;
|
||||||
|
|
||||||
var checkProcedure = plt.baselib.check.checkProcedure;
|
var checkProcedure = plt.baselib.check.checkProcedure;
|
||||||
|
|
||||||
|
@ -543,7 +577,7 @@
|
||||||
isWorldHandler,
|
isWorldHandler,
|
||||||
'world handler');
|
'world handler');
|
||||||
|
|
||||||
var checkView = plt.baselib.check.makeCheckArgumentType(
|
var checkMockView = plt.baselib.check.makeCheckArgumentType(
|
||||||
isMockView, 'view');
|
isMockView, 'view');
|
||||||
|
|
||||||
|
|
||||||
|
@ -654,7 +688,7 @@
|
||||||
'view-focus',
|
'view-focus',
|
||||||
2,
|
2,
|
||||||
function(MACHINE) {
|
function(MACHINE) {
|
||||||
var view = checkView(MACHINE, 'view-focus', 0);
|
var view = checkMockView(MACHINE, 'view-focus', 0);
|
||||||
var selector = checkSelector(MACHINE, 'view-focus', 1);
|
var selector = checkSelector(MACHINE, 'view-focus', 1);
|
||||||
try {
|
try {
|
||||||
return view.updateFocus(selector);
|
return view.updateFocus(selector);
|
||||||
|
@ -662,8 +696,8 @@
|
||||||
plt.baselib.exceptions.raise(
|
plt.baselib.exceptions.raise(
|
||||||
MACHINE,
|
MACHINE,
|
||||||
new Error(plt.baselib.format.format(
|
new Error(plt.baselib.format.format(
|
||||||
"unable to focus to ~s",
|
"unable to focus to ~s: ~s",
|
||||||
[selector])));
|
[selector, e.message])));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -671,7 +705,7 @@
|
||||||
'view-focus',
|
'view-focus',
|
||||||
1,
|
1,
|
||||||
function(MACHINE) {
|
function(MACHINE) {
|
||||||
var view = checkView(MACHINE, 'view-focus', 0);
|
var view = checkMockView(MACHINE, 'view-focus', 0);
|
||||||
return view.getText();
|
return view.getText();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -680,8 +714,8 @@
|
||||||
'update-view-text',
|
'update-view-text',
|
||||||
2,
|
2,
|
||||||
function(MACHINE) {
|
function(MACHINE) {
|
||||||
var view = checkView(MACHINE, 'update-view-text', 0);
|
var view = checkMockView(MACHINE, 'update-view-text', 0);
|
||||||
var text = checkString(MACHINE, 'update-view-text', 1);
|
var text = plt.baselib.format.toDisplayedString(MACHINE.env[MACHINE.env.length - 2]);
|
||||||
return view.updateText(text);
|
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