From 59b8a104c2a817307eb23ae44e5bf2afff97ed85 Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Wed, 7 Sep 2011 15:27:01 -0400 Subject: [PATCH 1/4] changing the comparision here to pointer equality --- js-assembler/runtime-src/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js-assembler/runtime-src/runtime.js b/js-assembler/runtime-src/runtime.js index 9baf50a..fc7d328 100644 --- a/js-assembler/runtime-src/runtime.js +++ b/js-assembler/runtime-src/runtime.js @@ -363,7 +363,7 @@ var marks = frame.marks; var i; for (i = 0; i < marks.length; i++) { - if (equals(key, marks[i][0])) { + if (key === marks[i][0]) { marks[i][1] = value; return; } From cc4bcb341ea5e02e2ece9ed32304ba067d243fa1 Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Wed, 7 Sep 2011 15:35:12 -0400 Subject: [PATCH 2/4] actions are a list, to reduce garbage --- js-assembler/runtime-src/baselib-lists.js | 25 ++++++++++++++++------- web-world/js-impl.js | 20 +++++++++++++----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/js-assembler/runtime-src/baselib-lists.js b/js-assembler/runtime-src/baselib-lists.js index 9b0ba06..a4ad775 100644 --- a/js-assembler/runtime-src/baselib-lists.js +++ b/js-assembler/runtime-src/baselib-lists.js @@ -121,7 +121,7 @@ break; } } - if (p !== Empty.EMPTY) { + if (p !== EMPTY) { texts.push('.'); texts.push(baselib.format.toDisplayedString(p, cache)); } @@ -138,14 +138,14 @@ while (p instanceof Cons) { node.appendChild(baselib.format.toDomNode(p.first, cache)); p = p.rest; - if (p !== Empty.EMPTY) { + if (p !== EMPTY) { node.appendChild(document.createTextNode(" ")); } if (typeof (p) === 'object' && cache.containsKey(p)) { break; } } - if (p !== Empty.EMPTY) { + if (p !== EMPTY) { node.appendChild(document.createTextNode(".")); node.appendChild(document.createTextNode(" ")); node.appendChild(baselib.format.toDomNode(p, cache)); @@ -157,13 +157,13 @@ 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 makeList = function () { - var result = Empty.EMPTY, i; + var result = EMPTY, i; for (i = arguments.length - 1; i >= 0; i--) { 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 // Returns true if x is a list (a chain of pairs terminated by EMPTY). var isList = function (x) { - while (x !== Empty.EMPTY) { + while (x !== EMPTY) { if (x instanceof Cons) { x = x.rest; } else { @@ -229,6 +240,6 @@ exports.reverse = reverse; exports.length = length; exports.listRef = listRef; - + exports.listToArray = listToArray; }(this.plt.baselib)); \ No newline at end of file diff --git a/web-world/js-impl.js b/web-world/js-impl.js index 5a4de6c..3717fe0 100644 --- a/web-world/js-impl.js +++ b/web-world/js-impl.js @@ -80,6 +80,8 @@ } }; + var EMPTY_PENDING_ACTIONS = plt.baselib.lists.EMPTY; + ////////////////////////////////////////////////////////////////////// // A MockView provides a functional interface to the DOM. It @@ -88,17 +90,25 @@ // freshness of the MockView. var MockView = function(cursor, pendingActions, eventHandlers, nonce) { this.cursor = cursor; + + // (listof (view -> void)) this.pendingActions = pendingActions; + this.eventHandlers = eventHandlers; this.nonce = nonce; }; var isMockView = plt.baselib.makeClassPredicate(MockView); + MockView.prototype.getPendingActions = function() { + return plt.baselib.lists.listToArray(this.pendingActions).reverse(); + }; + + MockView.prototype.act = function(actionForCursor, actionForEventHandlers, actionForReal) { if (arguments.length !== 3) { throw new Error("act: insufficient arguments"); } return new MockView(actionForCursor(this.cursor), - this.pendingActions.concat([actionForReal]), + plt.baselib.lists.makePair(actionForReal, this.pendingActions), actionForEventHandlers(this.eventHandlers), this.nonce); }; @@ -441,7 +451,7 @@ View.prototype.getMockAndResetFocus = function(nonce) { this.focus = this.top; return new MockView(domToCursor($(this.top).get(0)), - [], + EMPTY_PENDING_ACTIONS, this.eventHandlers.slice(0), nonce); }; @@ -512,14 +522,14 @@ } catch (exn1) { return onFail(exn1); } - return onSuccess(new MockView(domToCursor(dom.get(0)), [], [], undefined)); + return onSuccess(new MockView(domToCursor(dom.get(0)), EMPTY_PENDING_ACTIONS, [], undefined)); } else { try { dom = $(plt.baselib.format.toDomNode(x)); } catch (exn2) { return onFail(exn2); } - return onSuccess(new MockView(domToCursor(dom.get(0)), [], [], undefined)); + return onSuccess(new MockView(domToCursor(dom.get(0)), EMPTY_PENDING_ACTIONS, [], undefined)); } }; @@ -1086,7 +1096,7 @@ function(newMockView) { if (newMockView.nonce === nonce) { var i; - var actions = newMockView.pendingActions; + var actions = newMockView.getPendingActions(); for (i = 0; i < actions.length; i++) { actions[i](view); } From 5d62bbd9b62fc3752db42c730a2c05281309ce17 Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Wed, 7 Sep 2011 16:22:23 -0400 Subject: [PATCH 3/4] initial test cases on views --- tests/more-tests/view.expected | 3 ++ tests/more-tests/view.rkt | 21 +++++++++++ tests/run-more-tests.rkt | 1 + web-world/impl.rkt | 2 + web-world/js-impl.js | 69 ++++++++++++++++++++++++++++++++++ web-world/racket-impl.rkt | 8 +++- 6 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/more-tests/view.expected create mode 100644 tests/more-tests/view.rkt diff --git a/tests/more-tests/view.expected b/tests/more-tests/view.expected new file mode 100644 index 0000000..e66ca31 --- /dev/null +++ b/tests/more-tests/view.expected @@ -0,0 +1,3 @@ +"" +"some text" +(html (head) (body (p "hello world, this is a test") (div (@ (id "a div")) "some text"))) diff --git a/tests/more-tests/view.rkt b/tests/more-tests/view.rkt new file mode 100644 index 0000000..57aeac5 --- /dev/null +++ b/tests/more-tests/view.rkt @@ -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"))) \ No newline at end of file diff --git a/tests/run-more-tests.rkt b/tests/run-more-tests.rkt index 6c21fbb..4a4e2a9 100644 --- a/tests/run-more-tests.rkt +++ b/tests/run-more-tests.rkt @@ -26,3 +26,4 @@ (test "more-tests/hello-bf.rkt") (test "more-tests/conform.rkt") (test "more-tests/earley.rkt") +(test "more-tests/view.rkt") diff --git a/web-world/impl.rkt b/web-world/impl.rkt index 0a0a896..2856220 100644 --- a/web-world/impl.rkt +++ b/web-world/impl.rkt @@ -74,4 +74,6 @@ xexp? xexp->dom + view->xexp + )) diff --git a/web-world/js-impl.js b/web-world/js-impl.js index 3717fe0..42292af 100644 --- a/web-world/js-impl.js +++ b/web-world/js-impl.js @@ -100,6 +100,10 @@ var isMockView = plt.baselib.makeClassPredicate(MockView); + MockView.prototype.toString = function() { + return "<#view>"; + }; + MockView.prototype.getPendingActions = function() { return plt.baselib.lists.listToArray(this.pendingActions).reverse(); }; @@ -1282,6 +1286,61 @@ 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; + } + }; + + + + + + + @@ -1734,5 +1793,15 @@ }); + EXPORTS['view->xexp'] = makePrimitiveProcedure( + 'view->xexp', + 1, + function(MACHINE) { + var mockView = checkMockView(MACHINE, 'view-hide', 0); + var domNode = mockView.cursor.top().node; + return domToXexp(domNode); + }); + + ////////////////////////////////////////////////////////////////////// }()); \ No newline at end of file diff --git a/web-world/racket-impl.rkt b/web-world/racket-impl.rkt index bdd1202..325da57 100644 --- a/web-world/racket-impl.rkt +++ b/web-world/racket-impl.rkt @@ -28,7 +28,9 @@ open-output-element xexp? - xexp->dom) + xexp->dom + view->xexp + ) (define (big-bang world . handlers) @@ -169,3 +171,7 @@ (define (xexp->dom x) (error 'xexp->dom "Please run in JavaScript context.")) + + +(define (view->xexp x) + (error 'view->xexp "Please run in JavaScript context.")) From 421310929f4ce73542be951e33c8c0d3c7d4c656 Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Wed, 7 Sep 2011 16:28:45 -0400 Subject: [PATCH 4/4] light edits in documentation to refer to make-launcher --- scribblings/manual.scrbl | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/scribblings/manual.scrbl b/scribblings/manual.scrbl index f6d73c8..27628d4 100644 --- a/scribblings/manual.scrbl +++ b/scribblings/manual.scrbl @@ -151,6 +151,13 @@ $ planet link dyoo whalesong.plt 1 0 whalesong 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}. Here's how to do this at the command @@ -158,24 +165,30 @@ line: @verbatim|{ $ raco setup -P dyoo whalesong.plt 1 0 }| -This should compile Whalesong, as well as set up the @filepath{whalesong} executable. -Any time the source code in @filepath{whalesong} changes, we should repeat -this @tt{raco setup} step again. +This should compile Whalesong. Any time the source code in +@filepath{whalesong} changes, we should repeat this @tt{raco setup} +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|{ $ ./whalesong -Expected one of the following: [build, get-runtime, get-javascript]. +Usage: whalesong [option ...] + 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 --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. -Note: whenever Whalesong's source code is updated from Github, please -re-run the @tt{raco setup}. Otherwise, Racket will try to recompile -Whalesong on every single use of Whalesong, which can be very -expensive. - +To repeat: whenever Whalesong's source code is updated from Github, +please re-run the @tt{raco setup} step. Otherwise, Racket will try to +recompile Whalesong on every single use, which can be very expensive.