merging in documentation from cs19 into the mainline docs
This commit is contained in:
parent
bd5f0ba6f8
commit
df51ff112d
|
@ -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|{
|
||||
<html>
|
||||
<head><title>My simple program</title></head>
|
||||
<body>
|
||||
<p>The current counter is: <span id="counter">fill-me-in</span></p>
|
||||
</body>
|
||||
</html>
|
||||
}|
|
||||
}
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user