HTML operations

Arc provides a large number of operations for generating HTML. The basic operations are gentag to generate a standalone tag such as <img>, and tag to generate an open/close tag pair surrounding something. In addition, Arc provides special-purpose functions for common HTML operations. Arc has many operations to support forms, as well as many table operations, as tables are its primary layout technique.

For example, the following code generates a simple page with text, formatting, and a link.

    (whitepage (prn "Hello world!") (para) (link "Click here") (prn "for") (prbold "more stuff"))

The basic model is gentag generates a stand-alone tag, tag generates a begin/end tag pair surrounding something, and a variety of operations can be used for many tags.

A tag in Arc can be defined with a tag spec, which is a tag followed by attributes and values. The syntax is slightly different for gentag and tag. gentag takes the tag, attributes and values as arguments, while tag takes the tag spec as a single argument followed by body code that outputs the tag content to stdout. For example:

arc> (tostring (gentag p style "mystyle"))
"<p style=\"mystyle\">"
arc> (tostring (tag (p style "mystyle") (pr "Content.") (pr "More content")))
"<p style=\"mystyle\">Content.More content</p>"

Most of the HTML generation in Arc is stdout-based, rather than return-value-based. A typical HTML operation in Arc outputs a tag to stdout and executes body code which outputs the tag contents to stdout. The return value is generally not useful. This programming model fits well with Arc's web server, which expects content to be written to stdout in many cases. Note that this programming model is different from the standard functional programming model, but it has the advantage that the outputs from multiple functions can be collected and concatenated. For example,

arc> (tostring (underline (prn "hello") (prn "world")))
"<u>hello\nworld\n</u>"
(In the examples, the code is wrapped in tostring to explicitly capture stdout for clarity, but tostring normally wouldn't be used when implementing web pages.)

However, some HTML operations don't collect output from the body code, but use explicit arguments. For example,

arc> (tostring (row 1 2))
"<tr><td>1</td><td>2</td></tr>"
Other HTML operations accept either a list of atom arguments, or body code that outputs to stdout, but not a mixture. For example,
arc> (tostring (td (pr "hi")))
"<td>hi</td>"
arc> (tostring (td "hi"))
"<td>hi</td>"
Arc's HTML generation is relatively inflexible. Tags can only use attributes that are explicitly registered in the attributes table. The only exception is the style attribute; all tags support that attribute. Other attributes are ignored with a comment in the created HTML code. The following table shows the attributes supported by Arc:
<a class=string href=string id=sym onclick=string rel=string>
<body alink=color bgcolor=color leftmargin=number link=color marginheight=number marginwidth=number topmargin=number vlink=color>
<font color=color face=string size=number>
<form action=string method=sym>
<hr color=color>
<img align=sym border=number height=number hspace=number src=string vspace=number width=number>
<input name=string size=number type=sym value=escaped>
<option selected=selected>
<rss version=string>
<select name=string>
<span align=string class=string id=sym>
<table bgcolor=color border=number cellpadding=number cellspacing=number width=string>
<td align=sym bgcolor=color class=string colspan=number valign=sym width=number>
<textarea cols=number name=string rows=number wrap=sym>
<tr bgcolor=color>
New tags do not need to be explicitly defined, but any desired attributes that are not listed above need to be defined using attribute. For instance to support the "class" attribute for img:
arc> (attribute img class opstring)
arc> (tostring (gentag img class "foo"))
"<img class=\"foo\">"

Generating HTML

br [n]
Prints n break tags.
>(br)
<br>

br2
Prints two break tags.
>(br2)
<br><br>

center [body ...]
Prints body in a center tag.
>(center (pr "hello"))
<center>hello</center>

hello
underline [body ...]
Prints body in an underline tag.
>(underline (pr "hello ") (pr "world"))
<u>hello world</u>

hello world
prbold [arg ...]
Prints a bold (b) tag around arg.
>(prbold "hello")
<b>hello</b>

hello
para [arg ...]
Prints a paragraph (p) tag followed by the args.
>(para "hello " "world")
<p>hello world

hello world

whitepage [body ...]
Creates a white page containing the body.
>(whitepage (prn "hello"))
<html><body bgcolor=#ffffff alink=#0000be>hello
</body>
</html>
errpage [arg ...]
Prints a white page containing the args.
>(errpage "hello " "world")
<html><body bgcolor=#ffffff alink=#0000be>hello world

</body></html>
new-hspace n
Prints a horizonal spacer using span.
>(new-hspace 3)
<span style="padding-left:3px" />
blank-url
Returns path to a blank spacer GIF (s.gif).
>(blank-url)
"s.gif"
hspace w
Prints a horizontal spacer image of width w.
>(hspace 3)
<img src="s.gif" height=1 width=3>
vspace h
Prints a vertical spacer image of height h.
>(vspace 3)
<img src="s.gif" height=3 width=0>
vhspace h w
Prints a spacer image of height h and width w.
>(vhspace 3 5)
<img src="s.gif" height=3 width=5>
nbsp
Prints a non-breaking space.
>(nbsp)
&nbsp;
link text [href [color]]
Prints an HTML link.
>(link "click here" "http://arcfn.com" orange)
<a href="http://arcfn.com"><font color=#ff6600>click here
</font></a>

click here
underlink text [dest]
Prints an HTML link with explicitly underlined text.
>(underlink "click here" "http://arcfn.com")
<a href="http://arcfn.com"><u>click here</u></a>

click here
shortlink url
Prints a link to url with http:// removed from the text.
>(shortlink "http://arcfn.com")
<a href="http://arcfn.com">arcfn.com</a>

arcfn.com
parafy str
Returns str with paragraph tags. A p tag is inserted after each blank line.
>(parafy "hello\n\nworld\npeople")
"hello\n\n<p>world\npeople"
spanclass classname [body ...]
Prints body in a span tag.
>(spanclass myclass (pr "hello ") (pr "world"))
<span class="myclass">hello world</span>
pagemessage text
Prints text followed by two breaks, if it is not nil.
>(pagemessage "hello")
hello
<br><br>

>(pagemessage nil)

Table operations

tab [body ...]
Prints body in a table tag.
>(tab (pr "hello"))
<table border=0>hello</table>
sptab [body ...]
Prints body in a table tag with 0 border, 0 cellpadding, and 7 cellspacing.
>(sptab (prrow "foo" 1))
<table style="border-spacing: 7px 0px;"><tr><td>foo</td>
<td align=right>1</td></tr></table>

foo1
widtable w [body ...]
Prints a single-cell table containing body. The table has width w.
>(widtable "30%" (pr "hello"))
<table width="30%"><tr><td>hello</td></tr></table>

hello
zerotable [body ...]
Prints body in a table tag with 0 border, cellpadding, and cellspacing.
>(zerotable (pr "hello"))
<table border=0 cellpadding=0 cellspacing=0>hello</table>

hello
tr [body ...]
Prints body in a tr tag.
>(prn "hello")
hello

row [arg ...]
Prints a table row. Prints a td tag around each arg, and a tr tag around the whole output.
>(row 1 "a" 'b)
<tr><td>1</td><td>a</td><td>b</td></tr>
prrow [arg ...]
Prints a td tag around each arg, and a tr tag around the whole output. Each arg that is a number is right-justified.
>(prrow 1 "a" 'b)
<tr><td align=right>1</td><td>a</td><td>b</td></tr>
spacerow h
Prints a spacer table row of height h.
>(spacerow 10)
<tr style="height:10px"></tr>
spanrow colspan [body ...]
Prints body in a tr and td tag with the given colspan.
>(spanrow 3 (pr "hello"))
<tr><td colspan=3>hello</td></tr>
trtd [body ...]
Prints body in tr and td tags. body can be atoms.
>(trtd (prn "hello"))
<tr><td>hello
</td></tr>
>(trtd "hello " "world")
<tr><td>hello world</td></tr>
td [body ...]
Prints body in a td tag. body can be atoms.
>(td (prn "hello"))
<td>hello
</td>
>(td "hello " "world")
<td>hello world</td>
tdr [body ...]
Prints body in a td tag with alignment set to the right. body can be atoms. Renamed from tdright in arc3.
>(tdr (prn "hello"))
<td align=right>hello
</td>
>(tdright "world")
Error: _tdright: undefined;
 cannot reference an identifier 
before its definition
  in module: top-level
  internal name
: _tdright

tdcolor color [body ...]
Prints body in a td tag with the specified bgcolor. body can be atoms.
>(tdcolor orange (prn 1))
<td bgcolor=#ff6600>1
</td>
>(tdcolor orange 1 "world")
<td bgcolor=#ff6600>1world</td>
cellpr content
Prints content or &nbsp; if the content is nil
>(cellpr 42)
42
>(cellpr nil)
&nbsp;

Forms

form action [body ...]
Prints body in a form tag.
>(form "http://arcfn.com" (pr "hello"))
<form method=post action="http://arcfn.com">hello</form>
submit [value]
Prints a submit tag.
>(submit "hello")
<input type=submit value="hello">

but [value [name]]
Prints a submit button tag.
>(but "foo" "bar")
<input type=submit name="bar" value="foo">

buts name [text ...]
Prints multiple submit button tags, one for each text.
>(buts "foo" "bar" "baz")
<input type=submit name="foo" value="bar"> 
<input type=submit name="foo" value="baz">

textarea name rows cols [body ...]
Prints body in a textarea tag.
>(textarea "foo" 1 40 (pr "Contents"))
<textarea name="foo" rows=1 cols=40>Contents</textarea>

menu name itemlist [sel]
Prints a menu with select and option. Each element of itemlist becomes an option. If sel is an item in itemlist, that item is marked as selected.
>(menu "foo" '(item1 item2 item3) 'item2)
<select name="foo"><option>item1</option>
<option selected>item2</option><option>item3</option>
</select>

input name [val [size ]]
Prints an input tag.
>(input "foo" "bar" 10)
<input type=text name="foo" value="bar" size=10>

inputs [name label size value ...]
Prints a table of labelled text rows. If size is an atom, a text input of the specified size is generated. If size is a list (rows cols), then a textarea of the specified size is generated. If the label is password a password input is generated. The label is followed by a colon.
>(inputs i1 "Foo" 10 "contents"
  p1 password 10 "pw"
  i2 "Bar" '(2 5) "stuff")
<table border=0><tr><td>Foo:</td><td>
<input type=text name="i1" size=10 value="contents"></td>
</tr><tr><td>password:</td><td>
<input type=password name="p1" size=10 value="pw"></td></tr>
<tr><td>Bar:</td><td><textarea name="i2" rows=2 cols=5>stuff
</textarea></td></tr></table>

Foo:
password:
Bar:
single-input label name size buttontext [password]
Prints a text or password input prefixed with label followed by a submit button.
>(single-input "Secret" 'i1 5 "Submit" 'password)
Secret<input type=password name="i1" size=5> 
<input type=submit value="Submit">

Secret

Other operations

attribute tag attribute f
Defines that tag can have attribute. f is on of the op... functions, defining the type of attribute.
>(attribute a class opsym)
#<procedure: opsym>
color r g b
Creates a color object with the specified red, green, and blue components (0 to 255)
>(color 255 0 128)
#hash((b . 128) (g . 0) (r . 255))
hex>color str
Converts a string of length 6 representing a hex triplet into a color object. Returns nil on failure.
>(hex>color "ff00cc")
#hash((b . 204) (g . 0) (r . 255))
gray n
Creates a color object representing a gray level. n is between 0 and 255.
>(gray 100)
#hash((b . 100) (g . 100) (r . 100))
hexrep color
Converts a color object to a hex triplet string.
>(hexrep orange)
"ff6600"
dehex str
Converts str from hex to integer. Returns nil if the conversion fails.
>(dehex "40")
64
>(dehex "0x12")
nil
gentag tag [attribute value ...]
Prints the tag.
>(gentag img src "foo.gif")
<img src="foo.gif">
tag spec [body ...]
Prints body surrounded by the tag specified by spec.
>(gentag a href "/index.html")
<a href="/index.html">

>(pr "click")
click
tag-if test spec [body ...]
If test is true, body is wrapped in the tag spec. Otherwise, body is printed without the tag.
>(tag-if t (underline) (pr "hi"))
<underline>hi</underline>
>(tag-if nil (underline) (pr "hi"))
hi
pr-escaped str
Prints str with <, >, ", and & HTML-escaped.
>(pr-escaped "abc< > \" ' &")
abc&#60; &#62; &#34; ' &#38;
eschtml str
Returns a string that is str with <, >, ", ', and & HTML-escaped.
>(eschtml "abc< > \" ' &")
"abc&#60; &#62; &#34; &#39; &#38;"
esc<>& str
Returns a string that is str with <, >, and & HTML-escaped.
>(esc<>& "abc< > \" ' &")
Error: _esc
<>: undefined;
 cannot reference an identifier before its de
finition
  in module: top-level
  internal name: _esc<>

cdata [body ...]
Prints body in a CDATA section.
>(cdata (pr "hello"))
<![CDATA[hello]]>
striptags str
Returns str without tags. Anything between angle brackets is removed.
>(striptags "abc<foo>def</foo>ghi")
"abcdefghi"

Internals

The implementation of HTML handling in Arc centers around the opmeths* table, which maps from a tag and attribute to one of the op... functions. These functions generate the code that generates the HTML to set an attribute.

One Arc design pattern used in the HTML code is to use multiple functions to generate the code, and then use these functions inside macros. This provides a simulation of first-class macros.

opcolor attribute color
Generates code to assign a color object to an attribute.
>(opcolor 'bgcolor orange)
(whenlet gs2254 #hash((b . 0) (g . 102) (r . 255)) (pr " bgcolor=#" (hexrep gs2254)))
opstring attribute string
Generates code to assign a string to an attribute, if the string is not nil.
>(opstring 'class "foo")
(aif "foo" (pr " class=\"" it #\"))
opnum attribute num
Generates code to assign a number to an attribute, if num is not nil.
>(opnum 'width 42)
(aif 42 (pr " width=" it))
opsym attribute val
Generates code to assign a value to an attribute.
>(opsym 'width 42)
(pr " width=" 42)
opesc attribute val
Generates code to assign a quoted value to an attribute. If val is a string, it is escaped with pr-escaped.
>(opesc 'foo 'val)
(awhen val (pr " foo=\"") (if (isa it (quote string)) (pr-escaped it) (pr it)) (pr #\"))
opsel attribute val
Generates code to set attribute to selected if val is true.
>(opsel 'foo 'bar)
(if bar (pr " selected"))
start-tag spec
Generates code to start a tag.
>(start-tag '(a href foo))
(do (pr "<a") (aif foo (pr " href=\"" it #\")) (pr ">"))
end-tag spec
Generates code to end a tag.
>(end-tag '(a href foo))
(pr "</a>")
tag-options tag options
Generates code to assign values to attributes. options is a list of attribute/value pairs. The allowable attributes are defined by opmeth, and style is allowed for all tags.
>(tag-options 'td '((bgcolor red) (width 42)))
((whenlet gs2255 red (pr " bgcolor=#" (hexrep gs2255))) " width=42")
opmeths*
Table indexed by list of tag and attribute.
>(opmeths* '(a href))
#<procedure: opstring>
opmeth tag attr
Looks up the HTML tag and attribute in opmeths*
>(opmeth 'a 'href)
#<procedure: opstring>

Copyright 2008 Ken Shirriff.