From df51ff112d1499929a806dbeda2cecc5f73ebcfd Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Wed, 31 Aug 2011 16:36:36 -0400 Subject: [PATCH] merging in documentation from cs19 into the mainline docs --- scribblings/manual.scrbl | 373 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 365 insertions(+), 8 deletions(-) diff --git a/scribblings/manual.scrbl b/scribblings/manual.scrbl index cb6db53..50ddbed 100644 --- a/scribblings/manual.scrbl +++ b/scribblings/manual.scrbl @@ -16,7 +16,8 @@ @(require (for-label (this-package-in js)) (for-label (this-package-in lang/base)) - (for-label (this-package-in resource))) + (for-label (this-package-in resource) + (for-label (this-package-in web-world)))) @@ -412,7 +413,7 @@ Given the name of a program, this builds a standalone executes the program in a web browser. The @filepath{.xhtml} should be self-contained, with an exception: if -the file uses any external resources by using +the file uses any external @tech{resource}s by using @racket[define-resource], those resources are written into the current working directory, if they do not already exist there. @@ -451,7 +452,7 @@ get-javascript depend on. @section{Including external resources} @defmodule/this-package[resource] -Programs may need to use external file resources that aren't +Programs may need to use external file @deftech{resource}s that aren't themselves Racket programs, but instead some other kind of data. Graphical programs will often use @filepath{.png}s, and web-related programs @filepath{.html}s, for example. Whalesong provides the @@ -461,7 +462,7 @@ these resources will be bundled alongside the JavaScript-compiled output. @defform[(define-resource id [path-string])]{ -Defines a resource with the given path name. +Defines a @tech{resource} with the given path name. For example, @codeblock|{ @@ -477,12 +478,12 @@ As a convenience, you can also write (define-resource humpback.png) }| which defines a variable named @racket[humpback.png] whose -resource is @filepath{humpback.png}. +@tech{resource} is @filepath{humpback.png}. @defproc[(resource->url [a-resource resource?]) string?]{ -Given a resource, gets a URL. +Given a @tech{resource}, gets a URL. For example, @codeblock|{ @@ -514,7 +515,363 @@ For example, -@section{The JavaScript API} + + + +@section{The web-world API} + +@defmodule/this-package[web-world] + +The @tt{web-world} library allows you to write functional event-driven +@link["http://world.cs.brown.edu"]{World} programs for the web; the +user defines functional callbacks to handle events, and receive and +consume a world argument. + +One difference introduced by the web is the web page itself: because +the page itself is a source of state, it too will be passed to +callbacks. This library presents a functional version of the DOM in +the form of a @tech{view}. + + +Let's demonstrate this by creating a basic ticker that counts on the +screen every second. + +The first thing we can do is mock up a web page with a user interface, like this. +@filebox["index.html"]{ +@verbatim|{ + + My simple program + +

The current counter is: fill-me-in

+ + +}| +} +We can even look at this in a standard web browser. + +Once we're happy with the statics of our program, we can inject dynamic behavior. +Write a file called @filepath{tick-tock.rkt} with the following content. +@filebox["tick-tock.rkt"]{ +@codeblock|{ +#lang planet dyoo/whalesong +(require (planet dyoo/whalesong/web-world) + (planet dyoo/whalesong/resource)) + +(define-resource index.html) + +;; draw: world view -> view +(define (draw world dom) + (update-view-text (view-focus dom "#counter") world)) + + +;; tick: world view -> world +(define (tick world dom) + (add1 world)) + + +;; stop?: world view -> boolean +(define (stop? world dom) + (> world 10)) + +(big-bang 0 + (initial-view index.html) + (to-draw draw) + (on-tick tick 1) + (stop-when stop?)) +}| +} + +Several things are happening here. +@itemize[ + +@item{We @racket[require] a few libraries to get us some additional +behavior; in particular, @racketmodname/this-package[web-world] to let +us write event-driven web-based programs, and @racketmodname/this-package[resource] +to give us access to external @tech{resource}s.} + +@item{We use @racket[define-resource] to refer to external files, like @filepath{index.html} that +we'd like to include in our program.} + +@item{We use @racket[big-bang] to start up a computation that +responses to events. In this example, that's clock ticks introduced +by @racket[on-tick], though because we're on the web, we can +bind to many other kinds of web events (by using @racket[view-bind]).} +] + + +@subsection{@racket[big-bang] and its options} +@declare-exporting/this-package[web-world] +@defproc[(big-bang [w world] + [h big-bang-handler] ...) world]{ +Start a big bang computation. The @racket[big-bang] consumes an initial world, +as well as several handlers to configure it, described next: +} + +@defproc[(initial-view [x any]) big-bang-handler]{ +Provide an initial view for the big-bang. Normally, @racket[x] will be a @tech{resource} +to a web page. +@codeblock|{ +... +(define-resource page1.html) +... +(big-bang ... + (initial-view page1.html)) +}| +} + + +@defproc[(stop-when [stop? ([w world] [dom view] -> boolean)]) big-bang-handler]{ +Tells @racket[big-bang] when to stop. +@codeblock|{ +... +(define-struct world (given expected)) +... + +;; stop?: world view -> boolean +(define (stop? world dom) + (string=? (world-given world) (world-expected world))) + +(big-bang ... + (stop-when stop?)) +}| +} + + +@defproc*[(((on-tick [tick-f ([w world] [v view] [e event]? -> world)] [delay real]) big-bang-handler) + ((on-tick [tick-f ([w world] [v view] [e event]? -> world)]) big-bang-handler))]{ +Tells @racket[big-bang] to update the world during clock ticks. + +By default, this will send a clock tick 28 times a second, but if +given @racket[delay], it will use that instead. +@codeblock|{ +... +;; tick: world dom -> world +(define (tick world view) + (add1 world)) + +(big-bang ... + (on-tick tick 5)) ;; tick every five seconds +}| +} + + +@defproc[(on-mock-location-change [location-f ([w world] [v view] [e event]? -> world)]) big-bang-handler]{ +Tells @racket[big-bang] to update the world during simulated movement. + +During the extent of a big-bang, a form widget will appear in the +@tt{document.body} to allow us to manually send location-changing +events. + +The optional @tech{event} argument will contain numbers for +@racket["latitude"] and @racket["longitude"]. +@codeblock|{ +... +;; move: world view event -> world +(define (move world dom event) + (list (event-ref event "latitude") + (event-ref event "longitude"))) +... +(big-bang ... + (on-mock-location-change move)) +}| +} + + +@defproc[(on-location-change [location-f ([w world] [v view] [e event]? -> world)]) big-bang-handler]{ +Tells @racket[big-bang] to update when the location changes, as +received by the +@link["http://dev.w3.org/geo/api/spec-source.html"]{Geolocation API}. + +The optional @tech{event} argument will contain numbers for +@racket["latitude"] and @racket["longitude"]. +@codeblock|{ +... +;; move: world view event -> world +(define (move world dom event) + (list (event-ref event "latitude") + (event-ref event "longitude"))) +... +(big-bang ... + (on-location-change move)) +}| +} + + + + +@defproc[(to-draw [draw-f ([w world] [v view] -> view)]) big-bang-handler]{ +Tells @racket[big-bang] how to update the rendering of the world. The draw +function will be called every time an event occurs. + +@codeblock|{ +... +(define-struct world (name age)) + +;; draw: world view -> view +(define (draw world dom) + (update-view-text (view-focus dom "#name-span") + (world-name world))) +... +(big-bang ... + (to-draw draw)) +}| +} + + + +@subsection{Views} +@declare-exporting/this-package[web-world] +A @deftech{view} is a functional representation of the browser DOM +tree. A view is always focused on an element, and the functions in +this subsection show how to traverse and manipulate the view. + + + +@defproc[(->view [x any]) view]{ + +Coerse a value into a view whose focus is on the topmost element. +Common values for @racket[x] include @tech{resource}s. +} + + +@defproc[(view-focus [v view] [selector String]) view]{ +Focuses the view on an element, given the @racket[selector]. The view +will be searched starting from the toplevelmost node. + +Selectors are currently restricted to @litchar{#id} selectors for the +moment. +} + + +@defproc[(view-left [v view]) view]{ +Move the focus to the previous sibling. +} +@defproc[(view-right [v view]) view]{ +Move the focus to the next sibling.} + +@defproc[(view-up [v view]) view]{ +Move the focus to the parent.} + +@defproc[(view-down [v view]) view]{ +Move the view to the first child.} + +@defproc[(view-text [v view]) string]{ +Get the textual content at the focus. +} +@defproc[(update-view-text [v view] [s string]) view]{ +Update the textual content at the focus.} + +@defproc[(view-bind [v view] [type string] [world-updater ([w world] [v view] [e event]? -> world)]) view]{ +Attach a world-updating event to the focus. + +Attach a world-updating event to the focus. When the world-updater is +called, the view will be focused on the element that triggered the +event. + +Common event types include @racket["click"], @racket["mouseenter"], @racket["change"].} + +@defproc[(view-show [v view]) view]{ +Show the element at the focus. +} +@defproc[(view-hide [v view]) view]{ +Hide the element at the focus. +} + +@defproc[(view-attr [v view] [name String]) view]{ +Get the attribute @racket[name] at the focus. +} + +@defproc[(update-view-attr [v view] [name String] [value String]) view]{ +Update the attribute @racket[n] with the value @racket[v] at the focus. +} + +@defproc[(view-id [v view]) world]{ +Get the unique identifier of the node at the focus. +} + +@defproc[(view-form-value [v view]) view]{ +Get the form value of the node at the focus.} + +@defproc[(update-view-form-value [v view] [value String]) view]{ +Update the form value of the node at the focus.} + +@defproc[(view-append-child [d dom]) view]{ +Add the dom node @racket[d] as the last child of the focused node.} + + + +@subsection{Events} +@declare-exporting/this-package[web-world] +An @deftech{event} is a structure that holds name-value pairs. +Whenever an event occurs in web-world, it may include some auxiliary +information about the event. As a concrete example, location events +from @racket[on-location-change] and @racket[on-mock-location-change] +can send latitude and longitude values, as long as the world callback +can accept the event as an argument. + + +@defstruct[event ([kvpairs (listof (list symbol (or/c string number)))])]{} + +@defproc[(event-ref [evt event?] [name (or/c symbol string)]) value]{ +Get an value from the event, given its @racket[name]. +} + +@defproc[(event-keys [evt event?]) (listof symbol)]{ +Get an list of the event's keys. +} + + + +@subsection{Tips and tricks: Hiding standard output or directing it to an element} + +@declare-exporting/this-package[web-world] + +For a web-world program, output is normally done by using +@racket[to-draw]. However, side effecting functions, such as +@racket[printf] or @racket[display], are still available, and will +append to @tt{document.body}. + +We may want to disable such printing or redirect it to a particular +element on the page. For such purposes, use a combination of +@racket[current-output-port] and @racket[open-output-element] to +redirect the output of these side effect functions to somewhere else. + +For example: +@codeblock|{ +... +;; Redirect standard output to a div called "stdout-div". +(current-output-port (open-output-element "stdout-div")) +... +(big-bang ... + (on-tick (lambda (world dom) + (printf "Tick!\n") + (add1 world))) + ...) +}| + + +All subsequent I/O side effects after the call to +@racket[current-output-port] will be written out to the +@tt{stdout-div}, which can be easily styled with @tt{display: none} to +hide it from normal browser display. + + + +@defproc[(open-output-element [id string]) output-port]{ +Opens an output port that will be directed to write to the DOM element +whose id is @racket[id]. Note: writing to this port shouldn't fail, +even if the id does not currently exist on the page. +} + + + + + + + + + +@section{The JavaScript Foreign Function Interface} @defmodule/this-package[js]{ @@ -596,7 +953,7 @@ Returns the height of the viewport. @;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -@section{World programming} +@section{Simple world programming} @;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Whalesong provides a library to support writing functional I/O