+----------
Another confusing point about this is that the list structure matters:
if some `foo:' symbol doesn't appear at the beginning of a list, you
will not get the expected result -- normally, working with lists
(e.g., appending) does not modify the output, but if you modify the
list structure, you can get a mess:
+----------
|> (output-html (list "foo" (b: "bar")))
|foobar
|> (output-html (cons "foo" (b: "bar")))
|foob:bar
+----------
The second result turned out wrong, because the `b:' symbol that the
`b:' function created is no long at the beginning of a list. As said
above, remember that you can print these values to see what's wrong:
+----------
|> (list "foo" (b: "bar"))
|("foo" (b: "bar"))
|> (cons "foo" (b: "bar"))
|("foo" b: "bar")
+----------
The second expression's value shows why the output didn't come out
right.
There is a safety mechanism that can be used to prevent some of these
things -- making these HTML lists wrapped in an extra list level,
which will take care of most of these problem -- the
`make-safe-forms!' function can be used to turn it on (or off, given
an explicit #f argument):
+----------
|> (make-safe-forms!)
|> (b: "bar")
|((b: "bar"))
|> (cons "foo" (b: "bar"))
|("foo" (b: "bar"))
|> (output-html (cons "foo" (b: "bar")))
|foobar
|> (make-safe-forms! #f)
|> (output-html (cons "foo" (b: "bar")))
|foob:bar
+----------
This is not on by default not only because it is less efficient (the
difference is negligible), but because it modifies the result structures
which means that code that must have it on should most likely be fixed
anyway. Also, note that it doesn't change the result of manually
constructed lists (obviously).
*** Naming conventions
There are various identifier naming conventions that are used throughout
the system -- some are just conventions and some gets special treatment.
* As said above, symbols that begin with a ":" are keyword -- they
evaluate to themselves.
* Keywords that begin with a ":" (= symbols that begin with a "::") are
`meta-keywords' which are treated differently in HTML structures:
normal keywords are used as tag attributes to display, but meta
keywords are not displayed -- they are reserved for various tag
processing functions, for output formatting and other information.
For example:
+----------
|> (output-html '(foo: :bar 1 2 3))
|2 3
|> (output-html '(foo: ::bar 1 2 3))
|2 3
|> (output-html '(foo: ::spaces? #f ::bar 1 2 3))
|23
+----------
(As usual in Scheme, a "?" is used for booleans.)
* Symbols that end with a ":" are used for HTML tags. This is also for
symbols that are bound to functions that produce HTML tags with the
same symbol at its beginning.
* Names that end with a "~" are functions that generate HTML but do not
get keyword/value arguments -- usually these create some HTML
structure given some fixed arguments (e.g., `mailto~'), or HTML tags
that have no end-tag (e.g., `meta-content~'). They are usually bound
as the same type of functions (returning a value that looks like the
function call), so they can be planted in lists, but only defined ones
can be used.
* Names that end with a "~:" get some special argument and then more
arguments that are used as usual, e.g., `color~:'.
* Names that end with a "::" (or "~::") are functions that generate
functions that generate HTML output. These are usually internal
functions, and not defined as HTML tags (so you can't put these
symbols in lists).
* Names that start with a "_" are normally used for global html-object
bindings (see `defhtml' below).
* There are other standard Scheme conventions that I will not discuss.
Just one thing that I use -- `*foo*' for parameter names, because
they're somewhat equivalent to special globals in Common Lisp.
*** Some standard meta attributes
The following is a list of meta attributes that are common to all HTML
tags (there are others which are used by various functions). Note that
some meta keywords are inherited to subforms, and some do not get passed
along -- in the descriptions, below, the default is that there is no
inheritance except when specified.
* `::args' -- This is a special meta argument that can have a list value
which will be added to the argument list. It is usually intended for
keyword/value pairs, but can be used as a general mechanism (e.g., a
substitute for apply). For example:
+----------
|> (output-html
| (body: ::args '(:bgcolor "blue" :text "yellow") "foo"))
|
|foo
|
|> (output-html
| (body: :bgcolor "black" ::args '(:bgcolor "blue" :text "yellow")
| "foo"))
|
|foo
|
|> (output-html ;; <- unreliable
| (body: ::args '(:bgcolor "blue" :text "yellow") :bgcolor "black"
| "foo"))
|
|foo
|
|> (output-html
| (body: ::args '(:bgcolor "blue" :text "yellow" "bar") "foo"))
|
|bar
|foo
|
+----------
Note that the place where these are stuck in is not something to rely
on -- I have one version that sticks it in place, and another that
puts it between the keywords and the body. So don't rely on
precedence with other keywords, and put normal arguments only if
`::args' is the last keyword.
* `::func' -- A function that will be used to process the body. The
relation of this function and the assigned HTML tag string is complex
so just don't use this.
* `::empty?' -- If #t, then this construct should not have any body and
an error is raised if it gets any. If it is #f, then when the body is
empty you still get both open and close tags. The default is neither,
which makes the output have both tags only if there is some body
present.
* `::1st-args' -- This should be a list of keywords. This construct
will pull an appropriate number of elements from the body and use them
as values for these keywords, which makes it a mechanism to generate
`foo~:' functions. Mostly for internal usage.
* `::arg-funcs' -- The value of this keyword should be an association
list of a keyword and a processing function. If this keyword appears
in an HTML function, the function will be applied on the HTML tag,
keyword name (a string) and the given value, and it should return two
values for the new name and value. The default value is the value of
the `*arg-funcs*' parameter which defaults to a list that will use
`relativize-path' on `:src' and `:href' arguments. To disable this
processing, you can use `::arg-funcs #f', but that will disable all
processing so it is better to disable processing only for one
attribute, for example: `::arg-funcs (list* :href #f (*arg-funcs*))'.
This keyword is inherited, so you can do this at any form to modify
everything in it.
* `::literal?' -- If true, then HTML post-processing is disabled for the
body (see above for more details). Default: #f. This meta-keyword is
not inherited, but it is not needed since the context of the body is
all literal. Also, once it is used, it is impossible to disable it in
nested forms by giving a #f value.
* `::verbatim?' -- If true, then spaces and newlines that are used for
pretty printing are not used. Default: #f.
* `::indent?' -- If true, make the body indented. This meta keyword is
inherited, default: #f
* `::newlines?' -- If true, put a `newline!:' between body elements.
Note that `:newlines?' can be false, while `::indent?' true, because
the contents can have things with newlines in them. This meta keyword
is inherited, default: same as `:indent?'.
* `::spaces?' -- Same for `space!:'s. This meta keyword is inherited,
default: the opposite of `::newlines?'.
*** Form functions and wrappers
(make-form ...)
This is the function which is used to create HTML list structures.
Usually `list*', but can be modified with `make-safe-forms!'. See
above.
(defform name: [string] ...)
This macro defines `name:' (a ":" suffix is required) as an HTML
structure generating function that outputs a "name" tag (or a
different tag if a string follows `name:'). Such declared forms can
be used as functions that will create a list that looks like the
function application, so they can be used as symbols in lists too. It
can specify keywords (both normal and meta) to use with this construct
-- but when the function is used, these keyword values can be
overridden. If the string is #f, then no tag gets printed, which is
useful only for formatting and applying a sub `::func' field (this is
how functions like `newlines:' are defined), and if it is a symbol it
will use that form for output. See the source for advanced usages.
+----------
|> (output-html '(foo: 1 2))
|1 2
|> (defform foo: :bar 1)
|> (output-html '(foo: 1 2))
|1 2
|> (output-html (foo: 1 2))
|1 2
|> (output-html (foo:))
|
|> (output-html (foo: :bar 2))
|
|> (defform foo :bar 1)
|defform: got a name that doesn't end with a colon at: foo in: [...]
|> (defform foo: "foooo" :bar 1)
|> (output-html (foo:))
|
|> (output-html (foo: foo:))
|
+----------
Note that the resulting tag can be used with no arguments which means
that it can be used as is (as shown in the last expression).
(defwrapper name: [string] ...)
(deftag name: [string] ...)
These are versions of `defform' that set the ::empty? value:
+----------
|> (defwrapper foo: :bar 1)
|> (output-html (foo: foo:))
|
|> (deftag foo: :bar 1)
|> (output-html (foo: foo:))
|output-form: `foo' got a non-empty body: (#).
+----------
(form~: tag ...)
(wrapper~: tag ...)
(tag~: tag ...)
These are special HTML functions that can be used to create arbitrary
HTML tags, given a first string argument (must be first, and must be a
string):
+----------
|> (output-html (wrapper~: "foo" :bar 1))
|
|> (output-html (tag~: "foo" :bar 1))
|
|> (output-html (form~: "foo" :bar 1))
|
|> (output-html (form~: "foo" :bar 1 1 2))
|1 2
|> (output-html (form~: "foo" :bar 1 ::indent? #t 1 2))
|
| 1
| 2
|
|> (output-html '(form~: "foo" :bar 1 ::spaces? #f 1 2))
|12
|> (output-html '(form~: #f :bar 1 ::spaces? #f 1 2))
|12
+----------
(form:->:: foo:), (form~:->~:: foo~:),
These are functions that gets a form function as its argument, and
returns a different form function, the first returns a wrapper
constructor with some default values. The second works on functions
that get a unique first argument. (This is like currying, except that
keywords given to the final result get precedence.)
+----------
|> (defwrapper foo: :bar 1)
|> (defwrapper foo~: 'recform: ::tag 'foo: ::1st-args ::n)
|> (define foo:: (form:->:: foo:))
|> (define foo~:: (form~:->~:: foo~:))
|> (output-html (foo: "zzz"))
|zzz
|> (output-html (foo~: 3 "zzz"))
|zzz
|> (output-html (foo~: 3 :bar 2 "zzz"))
|zzz
|> (output-html ((foo:: :x 1) :y 2 "bar"))
|bar
|> (output-html ((foo:: :x 1) :x 2 "bar"))
|bar
|> (output-html ((foo~:: 2 :x 1) :x 2 "bar"))
|bar
+----------
(recform: foo:)
This is a form function that expects `::tag' and `::n' arguments, and
will repeat that tag the specified number of times, nesting it in
itself or collecting a result list (depending on the `::empty?'
property of its tag):
+----------
|> (output-html (recform: ::n 2 ::tag 'big:))
|
|> (output-html (recform: ::n 2 ::tag 'big: "A"))
|A
|> (output-html (recform: ::n 2 ::tag 'br:))
|
|> (defwrapper foo~: 'recform: ::tag 'foo: ::1st-args ::n)
|> (output-html (foo~: 2 "A"))
|A
|> (output-html (foo~: 2))
|
+----------
This is useful for HTML tags that can be `accumulated', like "",
and for defining many wrappers based on a more general one.
------------------------------------------------------------------------
3.7. Predefined tags and wrappers
This is a brief list of functions that generate HTML output. For more
details, see the source.
br:, break:, break~:
output an HTML line-break (" "). `break~:' can be used to output a
sequence of these.
hr: (hline:)
outputs a horizontal line ("").
html:, head:, body:, title:, link:, base:, frameset:, frame:, noframes:,
iframe:, meta:, p:, b:, i:, u:, em:, strong:, blink:, strike:, tt:,
cite:, code:, samp:, kbd:, dfn:, var:, abbr:, acronym:, h1:, h2:, h3:,
h4:, h5:, h6:, sub:, sup:, ins:, del:, nobr:
simple HTML wrappers and tags, each with some default formatting
attributes (`::newlines?', `::indent?', `::spaces?').
(link-rel~ rel ref)
(link-rev~ rev ref)
shorthand for (link: :rel rel :href ref), and the same for `:rev'.
(meta-content~ name content), (http-equiv~ name content)
shorthand for (meta: :name name :content content) and for
(meta: :http-equiv name :content content).
big:, big~:, small:, small~:
simple and `recform:' versions of these tags.
font:, face~:, size~:, color~:, size-2:, ..., size+4:, black:, white:,
red:, green:, blue:, cyan:, magenta:, yellow:, purple:
an HTML font tag, and shorthands for face, sizes, and colors
specifications.
div:, left:, right:, center:, justify:
a "div" tag, and shorthands for formatting text using the `:align'
attribute.
ltr:, rtl:
a "div" shorthand for using an "ltr" or an "rtl" direction using the
`:dir' attribute.
span:, class~:
a "span" tag, and shorthand for a "span" using a `:class' attribute.
address:, blockquote:, quote:, q:
more simple tags (quote: is a `blockquote:' tag).
pre:
a "pre" environment (with no automatic indentation).
img:, (image~ fname [alt] ...), (gif~ fname [alt] ...),
(jpg~ fname [alt] ...), (png~ fname [alt] ...)
an "img" tag, shorthand for specifying the `alt:' attribute (with
possibly more attributes), and shorthands for omitting common
suffixes.
(my-image~ fname [alt]), (my-gif~ fname [alt] ...),
(my-jpg~ fname [alt] ...), (my-png~ fname alt ...)
similar to the above, but automatically prepends `*image-dir*'.
map:, area:, spacer:
some more image-related things.
a:, (ref~: ref ...), (name~: label ...)
an html "a" wrapper, a reference link ("a" with an "href" attribute),
and a named anchor ("a" with a "name" attribute). The first argument
to `ref~:' and `label~:' is the URL and the rest is the keyword/values
and body expressions.
+----------
|> (output-html (html: (name~: "foo" "FOO") "foo..."
| (ref~: "#foo" "Go to FOO")))
|
|FOO
|foo...
|Go to FOO
|
+----------
http~:, ftp~:, telnet~:, mailto~:
shorthands that prepends the corresponding URL element to a `ref~:'
call.
(ref~ x), (http~ x), (ftp~ x), (telnet~ x), (mailto~ x)
generate links that contain their own text in tt font.
(list~: tag ...)
This generates a list of some kind, the given tag is the one used to
construct the list. There are some special keyword arguments that it
handles -- a `::br' argument specifies some number of " "s to stick
after every item to make a list with more spaces, and a `::subtag'
argument can be used to override the default "
" tag used for
items. The extra arguments for `list~:' are the items, each is a
list that is passed to the item wrapper. If the item list has a
`item>' symbol, then it is split by that symbol to generate the real
items (this is controlled by a `::split-by' argument). Note that
`item>' is bound to itself so it can be used unquoted and in manually
constructed lists.
+----------
|> (output-html (list~: "ul" '("item #1") '("item #2")))
|
+----------
There is also an option of using a second `::subtag2' argument for
things like description lists (below), and it is possible to put a
list of tags in `::subtag' and a matching list of separators in
`::split-by' (to change the token used to separate items) so a nested
list can be generated (for example, for tables). Finally, a
`::subargs' argument can be used to supply default arguments
(keywords) to sub items (see `table*:' below for an example).
enumerate:, itemize:, menu:, dir:
These functions use the above (through `list~::'), for lists with
"ol", "ul", "menu", and "dir" tags.
itemize-bullet:, itemize-circle:, itemize-square:
These are versions of `itemize:' that use "disc", "circle", and
"square" as the value of the "type" attribute to force a certain
marker.
description:
These are used for description lists ("dl"). Using them is similar
to the above, except that in items a `!>' separates the header part
("dt") from the body part ("dd") (`!>', like `item>' is bound to
itself).
table:, th:, tr:, td:
"table" and related wrappers.
table*:
This is an HTML function that uses the `list~:' function above, with
`row>' and `col>' tokens, to create a table:
+----------
|> (output-html (table*: :width "100%"
| row> :bgcolor "blue" col> "a" col> "b" "c"
| row> col> "x" "y"
| row> "1" "2" "3"))
|
+----------
Note the last item in all examples, which doesn't have `col>' tokens
so each element is made a separate item. The `table*:' form is much
easier to create `quick' tables, but when a program is used to create
complex tables, `table:' (with `tr:' and `td:') can be more
convenient, since it is not sensitive to the list structure of its
arguments.
form:, input:, button:, submit-button:, text-input:, checkbox:,
radiobox:, password-input:, hidden-input:, select:, option:
"form" wrapper and various form element tags, many are just `input:'
with some value for `:type' (the last two are actually wrappers
expecting a body).
(submit~: val ...)
shorthand for `submit-button:' with the first argument being the label
or the option value (the "value" attribute).
options:
Similar to a list, but generates a `select' element with nested
`options'. Each item should have the value first:
+----------
|> (output-html (options: item> 1 "item #1" item> 2 "item #2"))
|
+----------
(select-options: ...)
shorthand for a `select:' with nested `option:'s -- the arguments are
lists holding a value and a label, for example:
+----------
|> (output-html (select-options: '(1 label1) '(2 label2)))
|
+----------
(button*: ...)
this is the actual HTML "button" wrapper, unlike the above `button:'
which is just a short for (input: :type 'button ...).
(label~: label ...)
a "label" wrapper, with a value for the `:for' attribute.
(textarea: ...)
a "textarea" form element, the contents has indentation disabled.
legend:, (fieldset: ...)
a "legend" wrapper, and a "fieldset" wrapper -- if the `fieldset:'
body contains a `!>', then the first part will be sent to `legend:'
and the second used as the `fieldset:' body.
(comment: ...)
formatted HTML comment (doesn't accept any attributes):
+----------
|> (output-html (comment: "foo"))
|
|> (output-html (comment: "foo" "bar"))
|
+----------
(script-src~ src), (style-src~ css-file)
a "script" tag with its "src" attribute given and an empty body, and a
"link" tag used for a css file specification.
script:, style:, noscript:
comment-objects (objects that are placed in an HTML comment), and a
matching "noscript" wrapper -- the contents is protected from HTML
post-processing:
+----------
|> (output-html (script: "var i = 1;" "window.alert(\"foo\");"))
|
+----------
applet:, object:, param:, param~:
an "applet", and "object" wrappers, a "param" tag, and a function
receiving the name and value of the parameter.
embed:, noembed:
an "embed" tag and a "noembed" wrapper.
applet-params:, object-params:
Uses `list:' for items that each has a name and a value. Note that
this is also useful if you give it arguments that are a two-element
list each:
+----------
|> (output-html (applet-params: item> 'foo 1 item> 'bar 2))
|
|> (output-html (object-params: '(foo 1) '(bar 2)))
|
+----------
(html~: title head body [args...])
This is a convenient HTML wrapper, getting a title, a head argument
containing some list of stuff to put in the head section, and a body.
It also uses `*prefix*' to add stuff to the header section (#f by
default), and `*charset-type*' for adding the right meta-tag. In
addition, extra arguments can be used to override this behavior:
`:prefix' and `:charset-type' can be used to override these defaults.
+----------
|> (*prefix* (meta-content~ 'author (concat "Eli Barzilay")))
|> (output-html (html~: "Main Page" (list (script: "var foo = 1"))
| (body: "blah blah blah")))
|
|
|
|
|
| Main Page
|
|
|
|blah blah blah
|
|
+----------
Note that the head argument must be in a list, see `make-safe-forms!'
above.
(document: ...)
This is a global wrapper, it does some minimal job like outputting the
document type (if `*doc-type*' is set) and an optional comment to put
at the top and bottom of the result (if a `::comment' keyword is
given, or you can specify different ones with `::comment1' and
`::comment2').
========================================================================
4. Making it work
Now that working with all the above should be clear, we reach the point
of putting it all together in a script.
------------------------------------------------------------------------
4.1. General scripting
The easiest way to run a script in Unix, is to make it executable (using
"chmod +x foo") and write some `magic' prefix that will allow it to run.
What I found most convenient is the following prefix:
+----------
|#!/bin/sh
|#|
|exec mzscheme -r "$0" "$@"
||#
|... scheme code ...
+----------
This will make it a /bin/sh script that will just execute mzscheme with
the correct command-line arguments: `-r' is for running a script: short
for `-fmv-' which stands for `-f' for loading an argument, `-m' for
suppressing the banner, `-v' for no interactive read-eval-print loop,
and `--' to specify that more arguments are passed to the script without
further processing. The first "$0" argument is consumed by the `-f' --
this is the actual script (the double-quotes are to protect file name
with spaces from exploding to multiple arguments), and "$@" are other
arguments that are passed to the script because of the `--' (again "$@"
will send each argument, handling spaces in them correctly). When
MzScheme starts, it ignores the /bin/sh stuff and will happily proceed
to execute the file. Stick to this template and be safe.
On Windows, it should be possible to add an executable file type,
specifying how it should be run. For example, making it executed by
"C:\Program Files\PLT\MzScheme.exe" -r
should do the trick. Note that spaces makes life a mess.
------------------------------------------------------------------------
4.2. Script setup for "html.ss"
The MzScheme executable accepts lots of useful flags that can be used to
automate a lot of work. We've seen above how `-r' is used to run a
Scheme script, but HTML generation scripts will usually look roughly
like this:
+----------
|#!/bin/sh
|#|
|exec mzscheme -r "$0" "$@"
||#
|(require (lib "html.ss" "swindle"))
|...
|... HTML-definitions scheme code ...
|...
|(html-main argv)
+----------
(See below for `html-main' usage.)
Now, we can further use the following command-line arguments:
* `-L' to specify a library file and collection names;
* `-C' to invoke `main' on the given script arguments as a list
beginning with the script file name (it implies `-r').
Using these, we can write this equivalent script:
+----------
|#!/bin/sh
|#|
|exec mzscheme -LC "html.ss" "swindle" "$0" "$@"
||#
|...
|... HTML-definitions scheme code ...
|...
|(define (main args) (html-main (cdr args)))
+----------
Note that the "-LC" order specifies that the library and collection
names come first, then the script name and that `main' is invoked after
loading the file.
On Windows, the problem is that an extension can be used to specify that
a file is to be thrown as an argument on some binary with predefined
additional arguments. So either use something like the first script
above, or create yet another extension for HTML scripts.
------------------------------------------------------------------------
4.3. Creating HTML files
Finally, this section describes how to produce HTML files. This is
describing the level built on top of all of the above.
`*defined-htmls*' is a variable holding HTML objects. To define an HTML
object, use the `(defhtml foo ...)' macro -- it gets a variable name
that will be bound to the HTML object, and push that object onto
`*defined-html*'. The extra arguments are keyword/values, which are
expected to hold values for at least `:name' which holds the file name
(sans base directory and suffix, but possibly with sub directories), and
a `:contents' value that will be used to produce the HTML object. The
`:contents' value can be any of the valid value that is shown as usual,
or a function of a variable number of arguments that will be applied to
the supplied list of keywords and values so it can inspect values for
more keywords (e.g., supplied by defaults as described below). For
convenience, the `:name' argument can be dropped and the symbol name
will be used instead (dropping a "_" prefix of and adding "index" in
case it ends with a "/"). Also the `:contents' argument can be dropped
and its value should just be the last one -- in this case, its value
will be protected by a `delay' which makes it easier for it to reference
later defined values. `defhtml' uses the `(html-obj! ...)' macro,
which is similar to a simple `list', except that it handles the contents
value as described above, and the result is pushed on the
`*defined-htmls*' list (the `!' suffix doesn't mean that this is
especially exiting, just that it modifies a value rather than simply
returning one).
+----------
|> (html-obj! :a 1 :b 2 3)
|(:a 1 :b 2 :contents #)
|> (defhtml x :a 1 :b 2 3)
|> x
|(:name "x" :a 1 :b 2 :contents #)
|> (getarg x :contents)
|#
|> (force (getarg x :contents))
|3
|> *defined-htmls*
|((:name "x" :a 1 :b 2 :contents #)
| (:a 1 :b 2 :contents #))
+----------
One note about `:name' -- possible file arguments (e.g., arguments for
`ref~:'s and `image~', can be relativized in case `:name' contains a
subdirectory name; to do this, use the `relativize-path' function
described above. This means that to get it done properly, the actual
call should be done at HTML generation time (you can wrap them in a
`delay' or `thunk').
At this point you probably wonder why the whole keywords-values mess...
Well, one feature that is desired is to have access to information about
HTML objects from other pieces of code. For example, code that
generates a menu that shows links to other pages with some information
about them, which is included in the appropriate `defhtml' form. If an
HTML output object is used, there is no way to pull that information
out, so HTML objects are just keyword/value lists. A good setup is to
have a function that creates the contents function and treats given
information in some uniform way -- see below for an example. To pull
out keyword information from an HTML object, the utility function
`getarg' described above can be used. Remember the option of using
`delay' (a Scheme promise) or a thunk (a function with no arguments)
above -- putting references to yet-undefined HTML objects is possible in
such constructs since they get evaluated when all HTML objects are
defined.
To actually produce HTML files from HTML objects, use the `make-html'
function -- it should be called with an HTML object and possibly more
keywords/values. If keywords are provided, they will be used as default
values for HTML object keywords (so this can be used for invocation
specific values, like date). The `make-htmls' is similar, except it
expects a list of HTML objects as its first arguments (and again,
optionally more keywords/values). Finally, a convenient function is
`make-defined-htmls' -- when called (with no arguments or
keywords/values), it will go over all HTML objects in `*defined-htmls*',
collected by `defhtml' forms, and create their files. Actually, it will
empty `*defined-htmls*', and recheck it once it is done, since more HTML
objects may be defined while creating pages. `*all-html*' contains all
html object and is never emptied.
The following example is a complete script that will create three HTML
files in the directory you run it from. (The evaluation delay trick
should be used in case the body of the `_main' object references later
ones.)
+----------
|#!/bin/sh
|#|
|exec mzscheme -LC "html.ss" "swindle" "$0" "$@"
||#
|(define ((my-page: &rest body)
| &keys name (title (string-capital name)) info)
| (html: (title: title)
| (body: (h1: title) hr: body hr: (comment: "menu follows")
| (apply text: (map (lambda (html)
| (ref~: (concat (getarg html :name)
| (*html-suffix*))
| "[":(getarg html :info):"]"))
| (reverse *pages*))))))
|(defhtml _main :name "index" :title "Main" :info "My main page"
| (my-page: "Yo! Welcome to my main page!"))
|(defhtml _professional :info "Career stuff"
| (my-page: "As CEO of Foo Corp, I blah blah blah..."))
|(defhtml _hobbies :info "Personal hobbies"
| (my-page: "I like to shove dead bugs up my nose."))
|(define *pages* *defined-htmls*)
|(define (main args) (html-main (cdr args)))
+----------
(Remember that `defhtml' can use symbol names that contain `/'s to put
the results in these subdirectories, but remember that these
subdirectories should exist.)
This is the standard approach that I prefer -- a single script that
creates multiple HTML files. To create a single HTML file at a time,
different approaches can be used. The first one, is to have the same
script, but create only a subset of the defined HTML objects. For this,
use the function `html-main' instead of `make-defined-htmls'. This
function gets a list as an argument and will make each HTML object on
the list or search for it by a defined name in case of a string. It can
also accept more keyword/values to send to the html creation function.
If the input list is empty, it simply invokes `make-defined-htmls'. To
search an HTML object by a string name, it looks for either a symbol
bound to an HTML object with the given argument name, or an HTML object
with a `:name' attribute equal to the given argument, or an HTML object
whose processed name (the `:name' value with `*html-target-dir*' and
`*html-suffix*') is equal to the given argument. All this makes it
convenient to run on script inputs: with arguments it will generate only
the requested pages, and without it will create all defined pages.
Thing you should know if you use this approach:
* Make sure that the script header has that "$@" so additional command
line arguments are passed to MzScheme. On Windows you should make
sure that whatever way you chose to invoke MzScheme programs, should
be passing command line arguments to it.
* The variable MzScheme uses to bind command line arguments is `argv',
it is bound to a vector of strings but `html-main' will convert a
given vector to a list.
* MzScheme has a `-C' flag that will call the `main' function with the
list of arguments, beginning with the script file name -- so you can
bind `main' to `html-main' and get the above automatically.
* You should be aware of the fact that this approach might not evaluate
all HTML bodies, so you should be careful not to rely on an HTML
object evaluation to have a visible effect on evaluation of other
objects.
Another option is to create separate scripts to create several pages.
For this, you can either use the above functions (just have a single
HTML object defined on each script) or resort to lower level functions
like `output-html'. Another function that can be used for this purpose
is `(output-to-html "foo" html-object)' which will place the HTML output
of the given html-object in "foo.html" (or any other file, depending on
`*html-target-dir*' and `*html-suffix*'. If these scripts rely on some
shared code, you should consult the MzScheme documentation to learn how
to load files. Normally, you would just `load' the given shared code,
but you might want to invoke several scripts in a single execution --
for extra convenience you could use MzScheme's module mechanism. For a
more ambitious project, you can even use MzScheme's `make' library and
much more.
In addition to all this, remember that the HTML generators can do
anything at all. They can even read in external files and incorporate
them in the output -- remember that everything that is printed on the
standard output is included in the resulting HTML. You can use
`display-mixed-file' as described above, or do anything you want, (and
remember to use `literal:' where necessary).
========================================================================