Merge branch 'master' into dom

Conflicts:

	web-world/js-impl.js
This commit is contained in:
Danny Yoo 2011-09-07 16:32:46 -04:00
commit d19e8562ac
9 changed files with 161 additions and 24 deletions

View File

@ -121,7 +121,7 @@
break; break;
} }
} }
if (p !== Empty.EMPTY) { if (p !== EMPTY) {
texts.push('.'); texts.push('.');
texts.push(baselib.format.toDisplayedString(p, cache)); texts.push(baselib.format.toDisplayedString(p, cache));
} }
@ -138,14 +138,14 @@
while (p instanceof Cons) { while (p instanceof Cons) {
node.appendChild(baselib.format.toDomNode(p.first, cache)); node.appendChild(baselib.format.toDomNode(p.first, cache));
p = p.rest; p = p.rest;
if (p !== Empty.EMPTY) { if (p !== EMPTY) {
node.appendChild(document.createTextNode(" ")); node.appendChild(document.createTextNode(" "));
} }
if (typeof (p) === 'object' && cache.containsKey(p)) { if (typeof (p) === 'object' && cache.containsKey(p)) {
break; break;
} }
} }
if (p !== Empty.EMPTY) { if (p !== EMPTY) {
node.appendChild(document.createTextNode(".")); node.appendChild(document.createTextNode("."));
node.appendChild(document.createTextNode(" ")); node.appendChild(document.createTextNode(" "));
node.appendChild(baselib.format.toDomNode(p, cache)); node.appendChild(baselib.format.toDomNode(p, cache));
@ -157,13 +157,13 @@
var isPair = function (x) { return x instanceof Cons; }; var isPair = function (x) { return x instanceof Cons; };
var isEmpty = function (x) { return x === Empty.EMPTY; }; var isEmpty = function (x) { return x === EMPTY; };
var makePair = Cons.makeInstance; var makePair = Cons.makeInstance;
var makeList = function () { var makeList = function () {
var result = Empty.EMPTY, i; var result = EMPTY, i;
for (i = arguments.length - 1; i >= 0; i--) { for (i = arguments.length - 1; i >= 0; i--) {
result = Cons.makeInstance(arguments[i], result); result = Cons.makeInstance(arguments[i], result);
} }
@ -171,10 +171,21 @@
}; };
// Coerse a list back into a JavaScript array.
var listToArray = function(lst) {
var result = [];
while (lst !== EMPTY) {
result.push(lst.first);
lst = lst.rest;
}
return result;
};
// isList: Any -> Boolean // isList: Any -> Boolean
// Returns true if x is a list (a chain of pairs terminated by EMPTY). // Returns true if x is a list (a chain of pairs terminated by EMPTY).
var isList = function (x) { var isList = function (x) {
while (x !== Empty.EMPTY) { while (x !== EMPTY) {
if (x instanceof Cons) { if (x instanceof Cons) {
x = x.rest; x = x.rest;
} else { } else {
@ -229,6 +240,6 @@
exports.reverse = reverse; exports.reverse = reverse;
exports.length = length; exports.length = length;
exports.listRef = listRef; exports.listRef = listRef;
exports.listToArray = listToArray;
}(this.plt.baselib)); }(this.plt.baselib));

View File

@ -363,7 +363,7 @@
var marks = frame.marks; var marks = frame.marks;
var i; var i;
for (i = 0; i < marks.length; i++) { for (i = 0; i < marks.length; i++) {
if (equals(key, marks[i][0])) { if (key === marks[i][0]) {
marks[i][1] = value; marks[i][1] = value;
return; return;
} }

View File

@ -151,6 +151,13 @@ $ planet link dyoo whalesong.plt 1 0 whalesong
than the latest version that's on PLaneT at the time.) than the latest version that's on PLaneT at the time.)
Let's make the @filepath{whalesong} launcher somewhere appropriate. Run Racket with the following
@racket[require]:
@racketblock[
(require (planet dyoo/whalesong/make-launcher))
]
This will create a @filepath{whalesong} executable in the current working directory.
Finally, we need to set up Whalesong with @tt{raco setup}. Finally, we need to set up Whalesong with @tt{raco setup}.
Here's how to do this at the command Here's how to do this at the command
@ -158,24 +165,30 @@ line:
@verbatim|{ @verbatim|{
$ raco setup -P dyoo whalesong.plt 1 0 $ raco setup -P dyoo whalesong.plt 1 0
}| }|
This should compile Whalesong, as well as set up the @filepath{whalesong} executable. This should compile Whalesong. Any time the source code in
Any time the source code in @filepath{whalesong} changes, we should repeat @filepath{whalesong} changes, we should repeat this @tt{raco setup}
this @tt{raco setup} step again. step again.
At this point, you should be able to rung @filepath{whalesong} from the command line. At this point, you should be able to run the @filepath{whalesong} executable from the command line.
@verbatim|{ @verbatim|{
$ ./whalesong $ ./whalesong
Expected one of the following: [build, get-runtime, get-javascript]. Usage: whalesong <subcommand> [option ...] <arg ...>
where any unambiguous prefix can be used for a subcommand
The Whalesong command-line tool for compiling Racket to JavaScript
For help on a particular subcommand, use 'whalesong <subcommand> --help'
whalesong build build a standalone html and javascript package
whalesong get-runtime print the runtime library to standard output
whalesong get-javascript Gets just the JavaScript code and prints it to standard output
}| }|
and if this does appear, then Whalesong should be installed successfully. and if this does appear, then Whalesong should be installed successfully.
Note: whenever Whalesong's source code is updated from Github, please To repeat: whenever Whalesong's source code is updated from Github,
re-run the @tt{raco setup}. Otherwise, Racket will try to recompile please re-run the @tt{raco setup} step. Otherwise, Racket will try to
Whalesong on every single use of Whalesong, which can be very recompile Whalesong on every single use, which can be very expensive.
expensive.

View File

@ -0,0 +1,3 @@
""
"some text"
(html (head) (body (p "hello world, this is a test") (div (@ (id "a div")) "some text")))

21
tests/more-tests/view.rkt Normal file
View File

@ -0,0 +1,21 @@
#lang planet dyoo/whalesong
(require (planet dyoo/whalesong/web-world))
(define view (->view (xexp->dom `(html (head)
(body (p "hello world, this is a test")
(div (@ (id "a div"))))))))
(define new-view
(view-focus view "a div"))
(view-text new-view) ;; should be ""
(define updated-new-view
(update-view-text new-view "some text"))
(view-text updated-new-view) ;; should be "some text"
(view->xexp (view-up (view-up updated-new-view)))
;; should be:
; (html (head) (body (p "hello world, this is a test")
; (div (@ (id "a div")) "some text")))

View File

@ -26,3 +26,4 @@
(test "more-tests/hello-bf.rkt") (test "more-tests/hello-bf.rkt")
(test "more-tests/conform.rkt") (test "more-tests/conform.rkt")
(test "more-tests/earley.rkt") (test "more-tests/earley.rkt")
(test "more-tests/view.rkt")

View File

@ -74,4 +74,6 @@
xexp? xexp?
xexp->dom xexp->dom
view->xexp
)) ))

View File

@ -124,6 +124,8 @@
} }
}; };
var EMPTY_PENDING_ACTIONS = plt.baselib.lists.EMPTY;
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// A MockView provides a functional interface to the DOM. It // A MockView provides a functional interface to the DOM. It
@ -132,17 +134,29 @@
// freshness of the MockView. // freshness of the MockView.
var MockView = function(cursor, pendingActions, eventHandlers, nonce) { var MockView = function(cursor, pendingActions, eventHandlers, nonce) {
this.cursor = cursor; this.cursor = cursor;
// (listof (view -> void))
this.pendingActions = pendingActions; this.pendingActions = pendingActions;
this.eventHandlers = eventHandlers; this.eventHandlers = eventHandlers;
this.nonce = nonce; this.nonce = nonce;
}; };
var isMockView = plt.baselib.makeClassPredicate(MockView); var isMockView = plt.baselib.makeClassPredicate(MockView);
MockView.prototype.toString = function() {
return "<#view>";
};
MockView.prototype.getPendingActions = function() {
return plt.baselib.lists.listToArray(this.pendingActions).reverse();
};
MockView.prototype.act = function(actionForCursor, actionForEventHandlers, actionForReal) { MockView.prototype.act = function(actionForCursor, actionForEventHandlers, actionForReal) {
if (arguments.length !== 3) { throw new Error("act: insufficient arguments"); } if (arguments.length !== 3) { throw new Error("act: insufficient arguments"); }
return new MockView(actionForCursor(this.cursor), return new MockView(actionForCursor(this.cursor),
this.pendingActions.concat([actionForReal]), plt.baselib.lists.makePair(actionForReal, this.pendingActions),
actionForEventHandlers(this.eventHandlers), actionForEventHandlers(this.eventHandlers),
this.nonce); this.nonce);
}; };
@ -488,8 +502,9 @@
View.prototype.getMockAndResetFocus = function(nonce) { View.prototype.getMockAndResetFocus = function(nonce) {
this.focus = this.top; this.focus = this.top;
return new MockView(domToArrayTreeCursor($(this.top).get(0)), return new MockView(domToArrayTreeCursor($(this.top).get(0)),
[], EMPTY_PENDING_ACTIONS,
this.eventHandlers.slice(0), this.eventHandlers.slice(0),
nonce); nonce);
}; };
@ -560,14 +575,14 @@
} catch (exn1) { } catch (exn1) {
return onFail(exn1); return onFail(exn1);
} }
return onSuccess(new MockView(domToArrayTreeCursor(dom.get(0)), [], [], undefined)); return onSuccess(new MockView(domToArrayTreeCursor(dom.get(0)), EMPTY_PENDING_ACTIONS, [], undefined));
} else { } else {
try { try {
dom = $(plt.baselib.format.toDomNode(x)); dom = $(plt.baselib.format.toDomNode(x));
} catch (exn2) { } catch (exn2) {
return onFail(exn2); return onFail(exn2);
} }
return onSuccess(new MockView(domToArrayTreeCursor(dom.get(0)), [], [], undefined)); return onSuccess(new MockView(domToArrayTreeCursor(dom.get(0)), EMPTY_PENDING_ACTIONS, [], undefined));
} }
}; };
@ -1123,7 +1138,7 @@
function(newMockView) { function(newMockView) {
if (newMockView.nonce === nonce) { if (newMockView.nonce === nonce) {
var i; var i;
var actions = newMockView.pendingActions; var actions = newMockView.getPendingActions();
for (i = 0; i < actions.length; i++) { for (i = 0; i < actions.length; i++) {
actions[i](view); actions[i](view);
} }
@ -1309,6 +1324,61 @@
return false; return false;
}; };
var domToXexp = function(dom) {
var child, attrs, name, convertedChildren, i;
if (dom.nodeType === 1) {
attrs = plt.baselib.lists.EMPTY;
name = plt.baselib.symbols.makeSymbol(dom.nodeName.toLowerCase());
child = dom.firstChild;
convertedChildren = plt.baselib.lists.EMPTY;
for (i = 0; i < dom.attributes.length; i++) {
attrs = plt.baselib.lists.makePair(
plt.baselib.lists.makeList(plt.baselib.symbols.makeSymbol(dom.attributes[i].nodeName),
dom.attributes[i].nodeValue),
attrs);
}
while(child !== null) {
if (child.nodeType === 1) {
convertedChildren =
plt.baselib.lists.makePair(
domToXexp(child),
convertedChildren);
} else if (child.nodeType === 3) {
convertedChildren = plt.baselib.lists.makePair(
domToXexp(child),
convertedChildren);
}
// Ignore other types.
child = child.nextSibling;
}
if (attrs === plt.baselib.lists.EMPTY) {
return plt.baselib.lists.makePair(
name,
plt.baselib.lists.reverse(convertedChildren));
} else {
return plt.baselib.lists.makePair(
name,
plt.baselib.lists.makePair(
plt.baselib.lists.makePair(plt.baselib.symbols.makeSymbol("@"),
attrs),
plt.baselib.lists.reverse(convertedChildren)));
}
} else if (dom.nodeType === 3) {
return dom.nodeValue;
} else {
// If we can't convert it, return false.
return false;
}
};
@ -1761,5 +1831,15 @@
}); });
EXPORTS['view->xexp'] = makePrimitiveProcedure(
'view->xexp',
1,
function(MACHINE) {
var mockView = checkMockView(MACHINE, 'view-hide', 0);
var domNode = arrayTreeToDomNode(mockView.cursor.top().node);
return domToXexp(domNode);
});
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
}()); }());

View File

@ -28,7 +28,9 @@
open-output-element open-output-element
xexp? xexp?
xexp->dom) xexp->dom
view->xexp
)
(define (big-bang world . handlers) (define (big-bang world . handlers)
@ -169,3 +171,7 @@
(define (xexp->dom x) (define (xexp->dom x)
(error 'xexp->dom "Please run in JavaScript context.")) (error 'xexp->dom "Please run in JavaScript context."))
(define (view->xexp x)
(error 'view->xexp "Please run in JavaScript context."))