826 lines
45 KiB
HTML
826 lines
45 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html>
|
|
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html">
|
|
<title>CL-WHO - Yet another Lisp markup language</title>
|
|
<style type="text/css">
|
|
pre { padding:5px; background-color:#e0e0e0 }
|
|
h3, h4 { text-decoration: underline; }
|
|
a { text-decoration: none; padding: 1px 2px 1px 2px; }
|
|
a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
|
|
a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; }
|
|
a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
|
|
a.none { text-decoration: none; padding: 0; }
|
|
a.none:visited { text-decoration: none; padding: 0; }
|
|
a.none:hover { text-decoration: none; border: none; padding: 0; }
|
|
a.none:focus { text-decoration: none; border: none; padding: 0; }
|
|
a.noborder { text-decoration: none; padding: 0; }
|
|
a.noborder:visited { text-decoration: none; padding: 0; }
|
|
a.noborder:hover { text-decoration: none; border: none; padding: 0; }
|
|
a.noborder:focus { text-decoration: none; border: none; padding: 0; }
|
|
pre.none { padding:5px; background-color:#ffffff }
|
|
</style>
|
|
</head>
|
|
|
|
<body bgcolor=white>
|
|
|
|
<h2>CL-WHO - Yet another Lisp markup language</h2>
|
|
|
|
<blockquote>
|
|
<br> <br><h3><a name=abstract class=none>Abstract</a></h3>
|
|
|
|
There are plenty of <a
|
|
href="http://www.cliki.net/Lisp%20Markup%20Languages">Lisp Markup
|
|
Languages</a> out there - every Lisp programmer seems to write at
|
|
least one during his career - and CL-WHO (where <em>WHO</em> means
|
|
"with-html-output" for want of a better acronym) is probably
|
|
just as good or bad as the next one. They are all more or less similar
|
|
in that they provide convenient means to convert S-expressions
|
|
intermingled with code into (X)HTML, XML, or whatever but differ with
|
|
respect to syntax, implementation, and API. So, if you haven't made a
|
|
choice yet, check out the alternatives as well before you begin to use
|
|
CL-WHO just because it was the first one you came across. (Was that
|
|
repelling enough?) If you're looking for a slightly different approach
|
|
you might also want to look at <a
|
|
href="http://weitz.de/html-template/">HTML-TEMPLATE</a>.
|
|
<p>
|
|
I wrote this one in 2002 although at least Tim Bradshaw's <a
|
|
href="http://www.cliki.net/htout">htout</a> and <a
|
|
href="http://opensource.franz.com/aserve/aserve-dist/doc/htmlgen.html">AllegroServe's
|
|
HTML generation facilities</a> by John Foderaro of Franz Inc. were
|
|
readily available. Actually, I don't remember why I had to write my
|
|
own library - maybe just because it was fun and didn't take very long. The
|
|
syntax was obviously inspired by htout although it is slightly
|
|
different.
|
|
<p>
|
|
CL-WHO tries to create efficient code in that it makes constant
|
|
strings as long as possible. In other words, the code generated by the
|
|
CL-WHO macros will usually be a sequence of <code>WRITE-STRING</code>
|
|
forms for constant parts of the output interspersed with arbitrary
|
|
code inserted by the user of the macro. CL-WHO will make sure that
|
|
there aren't two adjacent <code>WRITE-STRING</code> forms with
|
|
constant strings. CL-WHO's output is
|
|
either XHTML (default), 'plain' (SGML) HTML or HTML5 (using HTML syntax) — depending on
|
|
what you've set <a href="index.html#html-mode"><code>HTML-MODE</code></a> to.
|
|
<p>
|
|
CL-WHO is intended to be portable and should work with all
|
|
conforming Common Lisp implementations. <a
|
|
href="index.html#mail">Let us know</a> if you encounter any
|
|
problems.
|
|
<p>
|
|
It comes with a <a
|
|
href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
|
|
license</a> so you can basically do with it whatever you want.
|
|
<p>
|
|
<font color=red>Download shortcut:</font> <a href="https://github.com/edicl/cl-who/releases">https://github.com/edicl/cl-who/releases</a>.
|
|
</blockquote>
|
|
|
|
<br> <br><h3><a class=none name="contents">Contents</a></h3>
|
|
<ol>
|
|
<li><a href="index.html#example">Example usage</a>
|
|
<li><a href="index.html#install">Download and installation</a>
|
|
<li><a href="index.html#support">Support</a>
|
|
<li><a href="index.html#syntax">Syntax and Semantics</a>
|
|
<li><a href="index.html#dictionary">The CL-WHO dictionary</a>
|
|
<ol>
|
|
<li><a href="index.html#with-html-output"><code>with-html-output</code></a>
|
|
<li><a href="index.html#with-html-output-to-string"><code>with-html-output-to-string</code></a>
|
|
<li><a href="index.html#*attribute-quote-char*"><code>*attribute-quote-char*</code></a>
|
|
<li><a href="index.html#*downcase-tokens-p*"><code>*downcase-tokens-p*</code></a>
|
|
<li><a href="index.html#*html-empty-tag-aware-p*"><code>*html-empty-tag-aware-p*</code></a>
|
|
<li><a href="index.html#*html-empty-tags*"><code>*html-empty-tags*</code></a>
|
|
<li><a href="index.html#*html-no-indent-tags*"><code>*html-no-indent-tags*</code></a>
|
|
<li><a href="index.html#*prologue*"><code>*prologue*</code></a>
|
|
<li><a href="index.html#esc"><code>esc</code></a>
|
|
<li><a href="index.html#fmt"><code>fmt</code></a>
|
|
<li><a href="index.html#htm"><code>htm</code></a>
|
|
<li><a href="index.html#str"><code>str</code></a>
|
|
<li><a href="index.html#html-mode"><code>html-mode</code></a>
|
|
<li><a href="index.html#escape-string"><code>escape-string</code></a>
|
|
<li><a href="index.html#escape-char"><code>escape-char</code></a>
|
|
<li><a href="index.html#*escape-char-p*"><code>*escape-char-p*</code></a>
|
|
<li><a href="index.html#escape-string-minimal"><code>escape-string-minimal</code></a>
|
|
<li><a href="index.html#escape-string-minimal-plus-quotes"><code>escape-string-minimal-plus-quotes</code></a>
|
|
<li><a href="index.html#escape-string-iso-8859-1"><code>escape-string-iso-8859-1</code></a>
|
|
<li><a href="index.html#escape-string-all"><code>escape-string-all</code></a>
|
|
<li><a href="index.html#escape-char-minimal"><code>escape-char-minimal</code></a>
|
|
<li><a href="index.html#escape-char-minimal-plus-quotes"><code>escape-char-minimal-plus-quotes</code></a>
|
|
<li><a href="index.html#escape-char-iso-8859-1"><code>escape-char-iso-8859-1</code></a>
|
|
<li><a href="index.html#escape-char-all"><code>escape-char-all</code></a>
|
|
<li><a href="index.html#conc"><code>conc</code></a>
|
|
<li><a href="index.html#convert-tag-to-string-list"><code>convert-tag-to-string-list</code></a>
|
|
<li><a href="index.html#convert-attributes"><code>convert-attributes</code></a>
|
|
</ol>
|
|
<li><a href="index.html#ack">Acknowledgements</a>
|
|
</ol>
|
|
|
|
<br> <br><h3><a name="example" class=none>Example usage</a></h3>
|
|
|
|
Let's assume that <code>*HTTP-STREAM*</code> is the stream your web
|
|
application is supposed to write to. Here are some contrived code snippets
|
|
together with the Lisp code generated by CL-WHO and the resulting HTML output.
|
|
|
|
<table border=0 cellspacing=10 width="100%">
|
|
|
|
<tr>
|
|
<td bgcolor="#e0e0e0" valign=top><pre>
|
|
(<a class=noborder href="index.html#with-html-output">with-html-output</a> (*http-stream*)
|
|
(loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa")
|
|
("http://marcusmiller.com/" . "Marcus Miller")
|
|
("http://www.milesdavis.com/" . "Miles Davis"))
|
|
do (<a class=noborder href="index.html#htm">htm</a> (:a :href link
|
|
(:b (str title)))
|
|
:br)))
|
|
</pre></td>
|
|
|
|
<td valign=top rowspan=2>
|
|
<a href='http://zappa.com/'><b>Frank Zappa</b></a><br /><a href='http://marcusmiller.com/'><b>Marcus Miller</b></a><br /><a href='http://www.milesdavis.com/'><b>Miles Davis</b></a><br />
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td bgcolor="#e0e0e0" valign=top><pre>
|
|
<font color="orange">;; code generated by CL-WHO (simplified)</font>
|
|
|
|
(let ((*http-stream* *http-stream*))
|
|
(progn
|
|
nil
|
|
(loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa")
|
|
("http://marcusmiller.com/" . "Marcus Miller")
|
|
("http://www.milesdavis.com/" . "Miles Davis"))
|
|
do (progn
|
|
(write-string "<a href='" *http-stream*)
|
|
(princ link *http-stream*)
|
|
(write-string "'><b>" *http-stream*)
|
|
(princ title *http-stream*)
|
|
(write-string "</b></a><br />" *http-stream*)))))
|
|
</pre></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td bgcolor="#e0e0e0" valign=top><pre>
|
|
(<a class=noborder href="index.html#with-html-output">with-html-output</a> (*http-stream*)
|
|
(:table :border 0 :cellpadding 4
|
|
(loop for i below 25 by 5
|
|
do (<a class=noborder href="index.html#htm">htm</a>
|
|
(:tr :align "right"
|
|
(loop for j from i below (+ i 5)
|
|
do (<a class=noborder href="index.html#htm">htm</a>
|
|
(:td :bgcolor (if (oddp j)
|
|
"pink"
|
|
"green")
|
|
(fmt "~@R" (1+ j))))))))))
|
|
</pre></td>
|
|
|
|
<td valign=top rowspan=2>
|
|
<table border='0' cellpadding='4'><tr align='right'><td bgcolor='green'>I</td><td bgcolor='pink'>II</td><td bgcolor='green'>III</td><td bgcolor='pink'>IV</td><td bgcolor='green'>V</td></tr><tr align='right'><td bgcolor='pink'>VI</td><td bgcolor='green'>VII</td><td bgcolor='pink'>VIII</td><td bgcolor='green'>IX</td><td bgcolor='pink'>X</td></tr><tr align='right'><td bgcolor='green'>XI</td><td bgcolor='pink'>XII</td><td bgcolor='green'>XIII</td><td bgcolor='pink'>XIV</td><td bgcolor='green'>XV</td></tr><tr align='right'><td bgcolor='pink'>XVI</td><td bgcolor='green'>XVII</td><td bgcolor='pink'>XVIII</td><td bgcolor='green'>XIX</td><td bgcolor='pink'>XX</td></tr><tr align='right'><td bgcolor='green'>XXI</td><td bgcolor='pink'>XXII</td><td bgcolor='green'>XXIII</td><td bgcolor='pink'>XXIV</td><td bgcolor='green'>XXV</td></tr></table>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td bgcolor="#e0e0e0" valign=top><pre>
|
|
<font color="orange">;; code generated by CL-WHO (simplified)</font>
|
|
|
|
(let ((*http-stream* *http-stream*))
|
|
(progn
|
|
nil
|
|
(write-string "<table border='0' cellpadding='4'>" *http-stream*)
|
|
(loop for i below 25 by 5
|
|
do (progn
|
|
(write-string "<tr align='right'>" *http-stream*)
|
|
(loop for j from i below (+ i 5)
|
|
do (progn
|
|
(write-string "<td bgcolor='" *http-stream*)
|
|
(princ (if (oddp j) "pink" "green") *http-stream*)
|
|
(write-string "'>" *http-stream*)
|
|
(format *http-stream* "~@r" (1+ j))
|
|
(write-string "</td>" *http-stream*)))
|
|
(write-string "</tr>" *http-stream*)))
|
|
(write-string "</table>" *http-stream*)))
|
|
</pre></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td bgcolor="#e0e0e0" valign=top><pre>
|
|
(<a class=noborder href="index.html#with-html-output">with-html-output</a> (*http-stream*)
|
|
(:h4 "Look at the character entities generated by this example")
|
|
(loop for i from 0
|
|
for string in '("Fête" "Sørensen" "naïve" "Hühner" "Straße")
|
|
do (<a class=noborder href="index.html#htm">htm</a>
|
|
(:p :style (<a href="index.html#conc">conc</a> "background-color:" (case (mod i 3)
|
|
((0) "red")
|
|
((1) "orange")
|
|
((2) "blue")))
|
|
(<a class=noborder href="index.html#htm">htm</a> (<a href="index.html#esc">esc</a> string))))))
|
|
</pre></td>
|
|
<td valign=top rowspan=2>
|
|
<h4>Look at the character entities generated by this example</h4><p style='background-color:red'>Fête</p><p style='background-color:orange'>Sørensen</p><p style='background-color:blue'>naïve</p><p style='background-color:red'>Hühner</p><p style='background-color:orange'>Straße</p>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td bgcolor="#e0e0e0" valign=top><pre>
|
|
<font color="orange">;; code generated by CL-WHO (simplified)</font>
|
|
|
|
(let ((*http-stream* *http-stream*))
|
|
(progn
|
|
nil
|
|
(write-string
|
|
"<h4>Look at the character entities generated by this example</h4>"
|
|
*http-stream*)
|
|
(loop for i from 0 for string in '("Fête" "Sørensen" "naïve" "Hühner" "Straße")
|
|
do (progn
|
|
(write-string "<p style='" *http-stream*)
|
|
(princ (<a class=noborder href="index.html#conc">conc</a> "background-color:"
|
|
(case (mod i 3)
|
|
((0) "red")
|
|
((1) "orange")
|
|
((2) "blue")))
|
|
*http-stream*)
|
|
(write-string "'>" *http-stream*)
|
|
(progn (write-string (<a class=noborder href="index.html#escape-string">escape-string</a> string) *http-stream*))
|
|
(write-string "</p>" *http-stream*)))))
|
|
</pre></td>
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
<br> <br><h3><a name="install" class=none>Download and installation</a></h3>
|
|
|
|
CL-WHO together with this documentation can be downloaded from <a
|
|
href="https://github.com/edicl/cl-who/releases">https://github.com/edicl/cl-who/releases</a>. The
|
|
current version is 1.1.5.
|
|
<p>
|
|
The preferred method to fetch, compile and load CL-WHO is via <a href="http://beta.quicklisp.org/">Quicklisp</a>. Install
|
|
Quicklisp, then run
|
|
<pre>(ql:quickload :cl-who)</pre>
|
|
<p>
|
|
The current development version of CL-WHO can be found
|
|
at <a href="https://github.com/edicl/cl-who">https://github.com/edicl/cl-who</a>.
|
|
This is the one to send <a href="index.html#mail">patches</a> against. Use at
|
|
your own risk.
|
|
<p>
|
|
Luís Oliveira maintains an
|
|
unofficial <a href="http://darcs.net/">darcs</a> repository of CL-WHO
|
|
at <a href="http://common-lisp.net/~loliveira/ediware/">http://common-lisp.net/~loliveira/ediware/</a>.
|
|
<p>
|
|
You can run a test suite which tests <em>some</em> (but
|
|
not <em>all</em>) aspects of the library with
|
|
<pre>
|
|
(asdf:oos 'asdf:test-op :cl-who)
|
|
</pre>
|
|
|
|
<br> <br><h3><a name="mail" class=none>Support and mailing lists</a></h3>
|
|
|
|
The development version of cl-who can be
|
|
found <a href="https://github.com/edicl/cl-who" target="_new">on
|
|
github</a>. Please use the github issue tracking system to submit bug
|
|
reports. Patches are welcome, please
|
|
use <a href="https://github.com/edicl/cl-who/pulls">GitHub pull
|
|
requests</a>. If you want to make a change,
|
|
please <a href="http://weitz.de/patches.html" target="_new">read this
|
|
first</a>.
|
|
|
|
<br> <br><h3><a name="syntax" class=none>Syntax and Semantics</a></h3>
|
|
|
|
CL-WHO is essentially just one <a
|
|
href="http://cl-cookbook.sourceforge.net/macros.html">macro</a>, <a
|
|
href="index.html#with-html-output"><code>WITH-HTML-OUTPUT</code></a>, which
|
|
transforms the body of code it encloses into something else obeying the
|
|
following rules (which we'll call <em>transformation rules</em>) for the body's forms:
|
|
|
|
<ul>
|
|
|
|
<li>A string will be printed verbatim. To be
|
|
more precise, it is transformed into a form which'll print this
|
|
string to the stream the user provides.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>"foo" <font color="red">=></font> (write-string "foo" s)</pre></td></tr></table>
|
|
|
|
(Here and for the rest of this document the <em>red arrow</em> means '... will be converted to code equivalent to ...' where <em>equivalent</em> means that all output is sent to the "right" stream.)
|
|
|
|
<li>Each list beginning with a <a
|
|
href="http://www.lispworks.com/reference/HyperSpec/Body/t_kwd.htm"><em>keyword</em></a>
|
|
is transformed into an (X)HTML <b>tag</b> of the same (usually <href="#*downcase-tokens-p*">downcased</a>) name by the following rules:
|
|
|
|
<ul>
|
|
|
|
<li>If the list contains nothing but the keyword, the resulting tag
|
|
will be empty.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:br) <font color="red">=></font> (write-string "<br />" s)</pre></td></tr></table>
|
|
With <a href="index.html#html-mode"><code>HTML-MODE</code></a> set to <code>:SGML</code> an empty element is written this way:
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:br) <font color="red">=></font> (write-string "<br>" s)</pre></td></tr></table>
|
|
|
|
<li>The initial keyword can be followed by another keyword which will be interpreted as the name of an <b>attribute</b>. The next form which will be taken as the attribute's <b>value</b>. (If there's no next form it'll be as if the next form had been <code>NIL</code>.) The form denoting the attribute's value will be treated as follows. (Note that the behaviour with respect to attributes is <em>incompatible</em> with versions earlier than 0.3.0!)
|
|
<ul>
|
|
<li>If it is a string it will be printed literally.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :bgcolor "red") <font color="red">=></font> (write-string "<td bgcolor='red' />" s)</pre></td></tr></table>
|
|
|
|
<li>If it is <code>T</code> and <a href="index.html#html-mode"><code>HTML-MODE</code></a> is <code>:XML</code> (default) the attribute's value will be the attribute's name (following XHTML convention to denote attributes which don't have a value in HTML).
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap t) <font color="red">=></font> (write-string "<td nowrap='nowrap' />" s)</pre></td></tr></table>
|
|
|
|
With <a href="index.html#html-mode"><code>HTML-MODE</code></a> set to <code>:SGML</code> or <code>:HTML5</code>:
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap t) <font color="red">=></font> (write-string "<td nowrap>" s)</pre></td></tr></table>
|
|
|
|
Attribute minimization is controlled by <a href="index.html#*empty-attribute-syntax*"><code>*EMPTY-ATTRIBUTE-SYNTAX*</code></a><br>
|
|
|
|
<li>If it is <code>NIL</code> the attribute will be left out completely.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap nil) <font color="red">=></font> (write-string "<td />" s)</pre></td></tr></table>
|
|
|
|
<li>If it is a <a
|
|
href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_c.htm#constant_form"><em>constant form</em></a>, the result of evaluating it will be inserted into the resulting string as if printed with the <a
|
|
href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_c.htm#constant_form">format string</a> <code>"~A"</code> at macro expansion time.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:table :border 3) <font color="red">=></font> (write-string "<table border='3' />" s)</pre></td></tr></table>
|
|
|
|
<li>If it is any other form it will be left as is and later evaluated at run time and printed like with <a
|
|
href="http://www.lispworks.com/reference/HyperSpec/Body/f_wr_pr.htm"><code>PRINC</code></a> <em>unless</em> the value is <code>T</code> or <code>NIL</code> which will be treated as above. (It is the application developer's job to provide the correct <a href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_p.htm#printer_control_variable">printer control variables</a>.)
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre><font color="orange">;; simplified example, see function CHECKBOX below
|
|
;; note that this form is not necessarily CONSTANTP in all Lisps</font>
|
|
|
|
(:table :border (+ 1 2)) <font color="red">=></font> (write-string "<table border='" s)
|
|
(princ (+ 1 2) s)
|
|
(write-string "' />" s)</pre></td></tr></table>
|
|
</ul>
|
|
|
|
<li>Once an attribute/value pair has been worked up another one can follow, i.e. if the form following an attribute's value is again a keyword it will again be treated as an attribute and so on.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:table :border 0 :cellpadding 5 :cellspacing 5)
|
|
<font color="red">=></font> (write-string "<table border='0' cellpadding='5' cellspacing='5' />" s)</pre></td></tr></table>
|
|
|
|
<li>The first form following either the tag's name itself or an attribute value which is <em>not</em> a keyword determines the beginning of the tag's <b>content</b>. This and all the following forms are subject to the transformation rules we're just describing.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:p "Paragraph") <font color="red">=></font> (write-string "<p>Paragraph</p>" s)
|
|
(:p :class "foo" "Paragraph") <font color="red">=></font> (write-string "<p class='foo'>Paragraph</p>" s)
|
|
(:p :class "foo" "One" " " "long" " " "sentence") <font color="red">=></font> (write-string "<p class='foo'>One long sentence</p>" s)
|
|
(:p :class "foo" "Visit " (:a :href "http://www.cliki.net/" "CLiki"))
|
|
<font color="red">=></font> (write-string "<p class='foo'>Visit <a href='http://www.cliki.net/'>CLiki</a></p>" s)</pre></td></tr></table>
|
|
|
|
<li>Beginning with <a href="index.html#install">version 0.4.0</a> you can also use a syntax like that of <a href="http://opensource.franz.com/xmlutils/xmlutils-dist/phtml.htm">LHTML</a> where the tag and all attribute/value pairs are enclosed in an additional list:
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>((:p) "Paragraph") <font color="red">=></font> (write-string "<p>Paragraph</p>" s)
|
|
((:p :class "foo") "Paragraph") <font color="red">=></font> (write-string "<p class='foo'>Paragraph</p>" s)
|
|
((:p :class "foo" :name "humpty-dumpty") "One" " " "long" " " "sentence")
|
|
<font color="red">=></font> (write-string "<p class='foo' name='humpty-dumpty'>One long sentence</p>" s)
|
|
((:p :class "foo") "Visit " ((:a :href "http://www.cliki.net/") "CLiki"))
|
|
<font color="red">=></font> (write-string "<p class='foo'>Visit <a href='http://www.cliki.net/'>CLiki</a></p>" s)</pre></td></tr></table>
|
|
|
|
</ul>
|
|
|
|
Here's a slightly more elaborate example:
|
|
<pre>
|
|
* (defun checkbox (stream name checked &optional value)
|
|
(with-html-output (stream)
|
|
(:input :type "checkbox" :name name :checked checked :value value)))
|
|
|
|
CHECKBOX
|
|
* (with-output-to-string (s) (checkbox s "foo" t))
|
|
|
|
"<input type='checkbox' name='foo' checked='checked' />"
|
|
* (with-output-to-string (s) (checkbox s "foo" nil))
|
|
|
|
"<input type='checkbox' name='foo' />"
|
|
* (with-output-to-string (s) (checkbox s "foo" nil "bar"))
|
|
|
|
"<input type='checkbox' name='foo' value='bar' />"
|
|
* (with-output-to-string (s) (checkbox s "foo" t "bar"))
|
|
|
|
"<input type='checkbox' name='foo' checked='checked' value='bar' />"
|
|
</pre>
|
|
|
|
<li>A keyword alone will be treated like a list containing only this keyword.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>:hr <font color="red">=></font> (write-string "<hr />" s)</pre></td></tr></table>
|
|
|
|
<li>A form which is neither a string nor a keyword nor a list beginning with a keyword will be left as is except for the following <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm#macrolet">local macros</a>:
|
|
<ul>
|
|
<li>Forms that look like <code>(<b>str</b> <i>form</i>)</code> will be substituted with
|
|
<span style="white-space: nowrap"><code>(let ((result <i>form</i>)) (when result (princ result s)))</code></span>.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 10 do (str i)) <font color="red">=></font>
|
|
(loop for i below 10 do
|
|
(let ((#:result i))
|
|
(when #:result (princ #:result *standard-output*))))</pre></td></tr></table>
|
|
|
|
<li>Forms that look like <code>(<b>fmt</b> <i>form*</i>)</code> will be substituted with <code>(format s <i>form*</i>)</code>.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 10 do (fmt "~R" i)) <font color="red">=></font> (loop for i below 10 do (format s "~R" i))</pre></td></tr></table>
|
|
<li>Forms that look like <code>(<b>esc</b> <i>form</i>)</code> will be substituted with
|
|
<span style="white-space: nowrap"><code>(let ((result <i>form</i>)) (when result (write-string (<a href="index.html#escape-string">escape-string</a> result s))))</code></span>.
|
|
|
|
<li>If a form looks like <code>(<b>htm</b> <i>form*</i>)</code> then each of the <code><i>forms</i></code> will be subject to the transformation rules we're just describing, i.e. this is the body is wrapped with another invocation of <a href="index.html#with-html-output"><code>WITH-HTML-OUTPUT</code></a>.
|
|
|
|
<table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 100 do (htm (:b "foo") :br))
|
|
<font color="red">=></font> (loop for i below 100 do (progn (write-string "<b>foo</b><br />" s)))</pre></td></tr></table>
|
|
|
|
|
|
</ul>
|
|
|
|
<li>That's all. Note in particular that CL-WHO knows <em>nothing</em> about HTML or XHTML, i.e. it doesn't check whether you mis-spelled tag names or use attributes which aren't allowed. CL-WHO doesn't care if you use, say, <code>:foobar</code> instead of <code>:hr</code>.
|
|
</ul>
|
|
|
|
<br> <br><h3><a class=none name="dictionary">The CL-WHO dictionary</a></h3>
|
|
|
|
CL-WHO exports the following symbols:
|
|
|
|
<p><br>[Macro]
|
|
<br><a class=none name="with-html-output"><b>with-html-output</b> <i>(var <tt>&optional</tt> stream <tt>&key</tt> prologue indent) declaration* form*</i> => <i>result*</i></a>
|
|
|
|
<blockquote><br> This is the main macro of CL-WHO. It will transform
|
|
its body by the transformation rules described
|
|
in <a href="index.html#syntax"><em>Syntax and Semantics</em></a> such that the
|
|
output generated is sent to the stream denoted
|
|
by <code><i>var</i></code>
|
|
and <code><i>stream</i></code>. <code><i>var</i></code> must be a
|
|
symbol. If <code><i>stream</i></code> is <code>NIL</code> it is
|
|
assumed that <code><i>var</i></code> is already bound to a stream,
|
|
if <code><i>stream</i></code> is
|
|
not <code>NIL</code> <code><i>var</i></code> will be bound to the
|
|
form <code><i>stream</i></code> which will be evaluated at run
|
|
time. <code><i>prologue</i></code> should be a string
|
|
(or <code>NIL</code> for the empty string which is the default) which
|
|
is guaranteed to be the first thing sent to the stream from within the
|
|
body of this macro. If <code><i>prologue</i></code> is <code>T</code>
|
|
the prologue string is the value
|
|
of <a href="index.html#*prologue*"><code>*PROLOGUE*</code></a>.
|
|
<p>
|
|
CL-WHO will usually try not to insert any unnecessary whitespace in
|
|
order to save bandwidth. However, if <code><i>indent</i></code>
|
|
is <em>true</em>, line breaks will be inserted and nested tags will be
|
|
indented properly. The value of <code><i>indent</i></code> - if it is
|
|
an integer - will be taken as the initial indentation. If it is not an
|
|
integer it is assumed to mean <code>0</code>. Value
|
|
of <a href="index.html#*html-no-indent-tags*"><code>*HTML-NO-INDENT-TAGS*</code></a>
|
|
controls which tag-contents are exempt from indentation: by default
|
|
contents of <code>PRE</code> and <code>TEXTAREA</code> tags are not
|
|
indented to avoid spurious layout changes. (Note: in certain
|
|
situations additional whitespace may change the layout of tables.)
|
|
<p>
|
|
The <code><i>results</i></code> are the values returned by
|
|
the <code><i>forms</i></code>.
|
|
<p>
|
|
Note that the keyword arguments <code><i>prologue</i></code>
|
|
and <code><i>indent</i></code>, and the associated variables are
|
|
used <em>at macro expansion time</em>.
|
|
|
|
<pre>
|
|
* (with-html-output (*standard-output* nil :prologue t)
|
|
(:html (:body "Not much there"))
|
|
(values))
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html><body>Not much there</body></html>
|
|
* (with-html-output (*standard-output*)
|
|
(:html (:body :bgcolor "white"
|
|
"Not much there"))
|
|
(values))
|
|
<html><body bgcolor='white'>Not much there</body></html>
|
|
* (with-html-output (*standard-output* nil :prologue t :indent t)
|
|
(:html (:body :bgcolor "white"
|
|
"Not much there"))
|
|
(values))
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html>
|
|
<body bgcolor='white'>
|
|
Not much there
|
|
</body>
|
|
</html>
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p><br>[Macro]
|
|
<br><a class=none name="with-html-output-to-string"><b>with-html-output-to-string</b> <i>(var <tt>&optional</tt> string-form <tt>&key</tt> element-type prologue indent) declaration* form*</i> => <i>result*</i></a>
|
|
|
|
<blockquote><br>
|
|
This is just a thin wrapper around <a href="index.html#with-html-output"><code>WITH-HTML-OUTPUT</code></a>. Indeed, the wrapper is so thin that the best explanation probably is to show its definition:
|
|
<pre>
|
|
(defmacro with-html-output-to-string ((var &optional string-form
|
|
&key (element-type ''character)
|
|
prologue
|
|
indent)
|
|
&body body)
|
|
"Transform the enclosed BODY consisting of HTML as s-expressions
|
|
into Lisp code which creates the corresponding HTML as a string."
|
|
`(with-output-to-string (,var ,string-form :element-type ,element-type)
|
|
(with-html-output (,var nil :prologue ,prologue :indent ,indent)
|
|
,@body)))
|
|
</pre>
|
|
Note that the <code><i>results</i></code> of this macro are determined by the behaviour of <a href="http://www.lispworks.com/reference/HyperSpec/Body/m_w_out_.htm"><code>WITH-OUTPUT-TO-STRING</code></a>.
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*attribute-quote-char*"><b>*attribute-quote-char*</b></a>
|
|
|
|
<blockquote><br>
|
|
This character is used as the quote character when building attributes. Defaults to the single quote <code>#\'</code>. Only other reasonable character is the double quote <code>#\"</code>.
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*downcase-tokens-p*"><b>*downcase-tokens-p*</b></a>
|
|
|
|
<blockquote><br>
|
|
If the value of this variable is <code>NIL</code>, keyword symbols representing a tag or attribute name will not be
|
|
automatically converted to lowercase. This is useful when one needs to
|
|
output case sensitive XML. The default is <code>T</code>.
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*html-empty-tag-aware-p*"><b>*html-empty-tag-aware-p*</b></a>
|
|
|
|
<blockquote><br>
|
|
Set this to <code>NIL</code> to if you want to use CL-WHO as a strict XML
|
|
generator. Otherwise, CL-WHO will only write empty tags listed in
|
|
<a href="index.html#*html-empty-tags*"><code>*HTML-EMPTY-TAGS*</code></a> as <code><tag/></code> (XHTML mode) or <code><tag></code> (SGML mode or HTML mode). For
|
|
all other tags, it will always generate <code><tag></tag></code>. The initial value of this variable is <code>T</code>.
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*html-empty-tags*"><b>*html-empty-tags*</b></a>
|
|
|
|
<blockquote><br>
|
|
The list of HTML tags that should be output as empty tags. See
|
|
<a href="index.html#*html-empty-tag-aware-p*"><code>*HTML-EMPTY-TAG-AWARE-P*</code></a>.
|
|
The initial value is the list
|
|
<pre>
|
|
(:area :atop :audioscope :base :basefont :br :choose :col :command :embed
|
|
:frame :hr :img :input :isindex :keygen :left :limittext :link :meta :nextid
|
|
:of :over :param :range :right :source :spacer :spot :tab :track :wbr)
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*html-no-indent-tags*"><b>*html-no-indent-tags*</b></a>
|
|
|
|
<blockquote><br>
|
|
The list of HTML tags that should disable indentation inside them even
|
|
when indentation is requested. The initial value is a list containing
|
|
only <code>:pre</code> and <code>:textarea</code>.
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*prologue*"><b>*prologue*</b></a>
|
|
|
|
<blockquote><br>
|
|
This is the prologue string which will be printed if the <code><i>prologue</i></code> keyword argument to <a href="index.html#with-html-output"><code>WITH-HTML-OUTPUT</code></a> is <code>T</code>. Gets changed when you set <a href="index.html#html-mode"><code>HTML-MODE</code></a>. Its initial value is
|
|
|
|
<pre>"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"</pre>
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*empty-attribute-syntax*"><b>*empty-attribute-syntax*</b></a>
|
|
|
|
<blockquote><br>
|
|
This controls the attribute minimization. (also called 'boolean attributes', or 'empty attribute syntax' according to the w3 html standard). Set value to <code>T</code> to enable attribute minimization.
|
|
<p>In XHTML attribute minimization is forbidden, and all attributes must have a value. Thus in XHTML boolean attributes must be defined as
|
|
<pre> <input disabled='disabled' /></pre>
|
|
In HTML5 and SGML HTML boolean attributes can be defined as
|
|
<pre><input disabled></pre>
|
|
Gets changed when you set <a href="index.html#html-mode"><code>HTML-MODE</code></a>. Its initial value is <code>NIL</code>
|
|
</p>
|
|
</blockquote>
|
|
|
|
<p><br>[Symbol]
|
|
<br><a class=none name="esc"><b>esc</b></a>
|
|
<br>[Symbol]
|
|
<br><a class=none name="fmt"><b>fmt</b></a>
|
|
<br>[Symbol]
|
|
<br><a class=none name="htm"><b>htm</b></a>
|
|
<br>[Symbol]
|
|
<br><a class=none name="str"><b>str</b></a>
|
|
|
|
<blockquote><br>
|
|
These are just symbols with no bindings associated with them. The only reason they are exported is their special meaning during the transformations described in <a href="index.html#syntax"><em>Syntax and Semantics</em></a>.
|
|
</blockquote>
|
|
|
|
<p><br>[Accessor]
|
|
<br><a class=none name="html-mode"><b>html-mode</b></a> <i>=> mode</i>
|
|
<br><tt>(setf (</tt><b>html-mode</b>) <i>mode</i><tt>)</tt>
|
|
<blockquote><br>
|
|
The function <code>HTML-MODE</code> returns the current mode for generating HTML. The default is <code>:XML</code> for XHTML. You can change this by setting it with <code>(SETF (HTML-MODE) :SGML)</code> to pre-XML HTML mode or <code>(SETF (HTML-MODE) :HTML5)</code> to HTML5 mode (using HTML syntax).
|
|
<p>
|
|
Setting it to SGML HTML sets the <a href="index.html#*prologue*"><code>*prologue*</code></a> to the doctype string for HTML 4.01 transitional:
|
|
<pre><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"></pre>
|
|
Code generation in HTML5 and SGML HTML is slightly different from XHTML - there's no need to end empty elements with <code>/></code> and empty attributes are allowed.
|
|
<p>
|
|
Setting it to HTML5 sets the <a href="index.html#*prologue*"><code>*prologue*</code></a> to the following doctype string:
|
|
<pre><!DOCTYPE html></pre>
|
|
</blockquote>
|
|
|
|
<p><br>[Function]
|
|
<br><a class=none name="escape-string"><b>escape-string</b></a> <i>string <tt>&key</tt> test</i> => <i>escaped-string</i>
|
|
|
|
<blockquote><br>
|
|
This function will accept a string <code><i>string</i></code> and will replace every character for which <code><i>test</i></code> returns <em>true</em> with its character entity. The numeric character entities use decimal instead of hexadecimal values when <a href="index.html#html-mode"><code>HTML-MODE</code></a> is set to <code>:SGML</code> because of compatibility reasons with old clients. <code><i>test</i></code> must be a function of one argument which accepts a character and returns a <a href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_g.htm#generalized_boolean">generalized boolean</a>. The default is the value of <a href="index.html#*escape-char-p*"><code>*ESCAPE-CHAR-P*</code></a>. Note the <a href="index.html#esc"><code>ESC</code></a> shortcut described in <a href="index.html#syntax"><em>Syntax and Semantics</em></a>.
|
|
|
|
<pre>
|
|
* (escape-string "<Hühner> 'naïve'")
|
|
"&lt;H&#xFC;hner&gt; &#x27;na&#xEF;ve&#x27;"
|
|
* (with-html-output-to-string (s)
|
|
(:b (esc "<Hühner> 'naïve'")))
|
|
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"<b>&lt;H&#xFC;hner&gt; &#x27;na&#xEF;ve&#x27;</b>"
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p><br>[Function]
|
|
<br><a class=none name="escape-char"><b>escape-char</b></a> <i>character <tt>&key</tt> test</i> => <i>escaped-string</i>
|
|
|
|
<blockquote><br>
|
|
This function works identical to <a href="index.html#escape-string"><code>ESCAPE-STRING</code></a>, except that it operates on characters instead of strings.
|
|
</blockquote>
|
|
|
|
<p><br>[Special variable]
|
|
<br><a class=none name="*escape-char-p*"><b>*escape-char-p*</b></a>
|
|
|
|
<blockquote><br>
|
|
This is the default for the <code><i>test</i></code> keyword argument to <a href="index.html#escape-string"><code>ESCAPE-STRING</code></a> and <a href="index.html#escape-char"><code>ESCAPE-CHAR</code></a>. Its initial value is
|
|
|
|
<pre>
|
|
#'(lambda (char)
|
|
(or (find char "<>&'\"")
|
|
(> (char-code char) 127)))
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p><br>[Function]
|
|
<br><a class=none name="escape-string-minimal"><b>escape-string-minimal</b> <i>string</i> => <i>escaped-string</i></a>
|
|
<br>[Function]
|
|
<br><a class=none name="escape-string-minimal-plus-quotes"><b>escape-string-minimal-plus-quotes</b> <i>string</i> => <i>escaped-string</i></a>
|
|
<br>[Function]
|
|
<br><a class=none name="escape-string-iso-8859-1"><b>escape-string-iso-8859-1</b> <i>string</i> => <i>escaped-string</i></a>
|
|
<br>[Function]
|
|
<br><a class=none name="escape-string-all"><b>escape-string-all</b> <i>string</i> => <i>escaped-string</i></a>
|
|
<br>[Function]
|
|
<br><a class=none name="escape-char-minimal"><b>escape-char-minimal</b> <i>character</i> => <i>escaped-string</i></a>
|
|
<br>[Function]
|
|
<br><a class=none name="escape-char-minimal-plus-quotes"><b>escape-char-minimal-plus-quotes</b> <i>character</i> => <i>escaped-string</i></a>
|
|
<br>[Function]
|
|
<br><a class=none name="escape-char-iso-8859-1"><b>escape-char-iso-8859-1</b> <i>character</i> => <i>escaped-string</i></a>
|
|
<br>[Function]
|
|
<br><a class=none name="escape-char-all"><b>escape-char-all</b> <i>character</i> => <i>escaped-string</i></a>
|
|
|
|
<blockquote><br> These are convenience function based
|
|
on <a href="index.html#escape-string"><code>ESCAPE-STRING</code></a>
|
|
and <a href="index.html#escape-char"><code>ESCAPE-CHAR</code></a>. The string
|
|
functions are defined in a way similar to this one:
|
|
|
|
<pre>
|
|
(defun escape-string-minimal (string)
|
|
"Escape only #\<, #\>, and #\& in STRING."
|
|
(escape-string string :test #'(lambda (char) (find char "<>&"))))
|
|
|
|
(defun escape-string-minimal-plus-quotes (string)
|
|
"Like ESCAPE-STRING-MINIMAL but also escapes quotes."
|
|
(escape-string string :test #'(lambda (char) (find char "<>&'\""))))
|
|
|
|
(defun escape-string-iso-8859-1 (string)
|
|
"Escapes all characters in STRING which aren't defined in ISO-8859-1."
|
|
(escape-string string :test #'(lambda (char)
|
|
(or (find char "<>&'\"")
|
|
(> (char-code char) 255)))))
|
|
|
|
(defun escape-string-all (string)
|
|
"Escapes all characters in STRING which aren't in the 7-bit ASCII
|
|
character set."
|
|
(escape-string string :test #'(lambda (char)
|
|
(or (find char "<>&'\"")
|
|
(> (char-code char) 127)))))
|
|
</pre>
|
|
The character functions are defined in an analogous manner.
|
|
</blockquote>
|
|
|
|
<p><br>[Function]
|
|
<br><a class=none name="conc"><b>conc</b> <i><tt>&rest</tt> string-list</i> => <i>string</i></a>
|
|
|
|
<blockquote><br>
|
|
Utility function to concatenate all arguments (which should be strings) into one string. Meant to be used mainly with attribute values.
|
|
|
|
<pre>
|
|
* (conc "This" " " "is" " " "a" " " "sentence")
|
|
"This is a sentence"
|
|
* (with-html-output-to-string (s)
|
|
(:div :style (conc "padding:"
|
|
(format nil "~A" (+ 3 2)))
|
|
"Foobar"))
|
|
"<div style='padding:5'>Foobar</div>"
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p><br>[Generic Function]
|
|
<br><a class=none name="convert-tag-to-string-list"><b>convert-tag-to-string-list</b></a> <i>tag attr-list body body-fn</i> => <i>strings-or-forms</i>
|
|
|
|
<blockquote><br>
|
|
|
|
This function exposes some of CL-WHO's internals so users can
|
|
customize its behaviour. It is called whenever a tag is processed and
|
|
must return a corresponding list of strings or Lisp forms. The idea
|
|
is that you can specialize this generic function in order to process
|
|
certain tags yourself.
|
|
<p>
|
|
<code><i>tag</i></code> is a keyword symbol naming the outer tag,
|
|
<code><i>attr-list</i></code> is an alist of its attributes (the car
|
|
is the attribute's name as a keyword, the cdr is its value),
|
|
<code><i>body</i></code> is the tag's body, and
|
|
<code><i>body-fn</i></code> is a function which should be applied to
|
|
the body to further process it. Of course, if you define your own
|
|
methods you can ignore <code><i>body-fn</i></code> if you want.
|
|
<p>
|
|
Here are some simple examples:
|
|
<pre>
|
|
* (defmethod convert-tag-to-string-list ((tag (eql :red)) attr-list body body-fn)
|
|
(declare (ignore attr-list))
|
|
(nconc (cons "<font color='red'>" (funcall body-fn body)) (list "</font>")))
|
|
; Compiling LAMBDA (PCL::.PV-CELL. PCL::.NEXT-METHOD-CALL. TAG ATTR-LIST BODY BODY-FN):
|
|
; Compiling Top-Level Form:
|
|
|
|
#<STANDARD-METHOD CONVERT-TAG-TO-STRING-LIST ((EQL :RED) T T T) {582B268D}>
|
|
* (with-html-output (*standard-output*)
|
|
(:red (:b "Bold and red"))
|
|
(values))
|
|
<font color='red'><b>Bold and red</b></font>
|
|
* (show-html-expansion (s)
|
|
(:red :style "spiffy" (if (foo) (htm "Attributes are ignored"))))
|
|
|
|
(LET ((S S))
|
|
(PROGN
|
|
NIL
|
|
(WRITE-STRING "<font color='red'>" S)
|
|
(IF (FOO) (PROGN (WRITE-STRING "Attributes are ignored" S)))
|
|
(WRITE-STRING "</font>" S)))
|
|
* (defmethod convert-tag-to-string-list ((tag (eql :table)) attr-list body body-fn)
|
|
(cond ((cdr (assoc :simple attr-list))
|
|
(nconc (cons "<table"
|
|
(<a class=noborder href="index.html#convert-attributes">convert-attributes</a> (remove :simple attr-list :key #'car)))
|
|
(list ">")
|
|
(loop for row in body
|
|
collect "<tr>"
|
|
nconc (loop for col in row
|
|
collect "<td>"
|
|
when (constantp col)
|
|
collect (format nil "~A" col)
|
|
else
|
|
collect col
|
|
collect "</td>")
|
|
collect "</tr>")
|
|
(list "</table>")))
|
|
(t
|
|
<font color=orange>;; you could as well invoke CALL-NEXT-METHOD here, of course</font>
|
|
(nconc (cons "<table "
|
|
(<a class=noborder href="index.html#convert-attributes">convert-attributes</a> attr-list))
|
|
(list ">")
|
|
(funcall body-fn body)
|
|
(list "</table>")))))
|
|
; Compiling LAMBDA (PCL::.PV-CELL. PCL::.NEXT-METHOD-CALL. TAG ATTR-LIST BODY BODY-FN):
|
|
; Compiling Top-Level Form:
|
|
|
|
#<STANDARD-METHOD CONVERT-TAG-TO-STRING-LIST ((EQL :TABLE) T T T) {58AFB7CD}>
|
|
* (with-html-output (*standard-output*)
|
|
(:table :border 0 (:tr (:td "1") (:td "2")) (:tr (:td "3") (:td "4"))))
|
|
<table border='0'><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table>
|
|
"</td></tr></table>"
|
|
* (show-html-expansion (s)
|
|
(:table :simple t :border 0
|
|
(1 2) (3 (fmt "Result = ~A" (compute-result)))))
|
|
|
|
(LET ((S S))
|
|
(PROGN
|
|
NIL
|
|
(WRITE-STRING
|
|
"<table border='0'><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>"
|
|
S)
|
|
(FORMAT S "Result = ~A" (COMPUTE-RESULT))
|
|
(WRITE-STRING "</td></tr></table>" S)))
|
|
</pre>
|
|
|
|
</blockquote>
|
|
|
|
<p><br>[Function]
|
|
<br><a class=none name="convert-attributes"><b>convert-attributes</b></a> <i>attr-list</i> => <i>strings-or-forms</i>
|
|
|
|
<blockquote><br>
|
|
|
|
This is a helper function which can be called from
|
|
<a href="index.html#convert-tag-to-string-list"><code>CONVERT-TAG-TO-STRING-LIST</code></a> to process the list of attributes.
|
|
|
|
</blockquote>
|
|
|
|
<br> <br><h3><a class=none name="ack">Acknowledgements</a></h3>
|
|
|
|
Thanks to Tim Bradshaw and John Foderaro for the inspiration provided
|
|
by their libraries mentioned <a href="index.html#abstract">above</a>. Thanks to
|
|
Jörg-Cyril Höhle for his suggestions with respect to
|
|
attribute values. Thanks to Kevin Rosenberg for the LHTML patch.
|
|
Thanks to Stefan Scholl for the 'old school' patch. Thanks to Mac
|
|
Chan for several useful additions.
|
|
|
|
<p>
|
|
$Header: /usr/local/cvsrep/cl-who/doc/index.html,v 1.68 2009/03/09 21:54:11 edi Exp $
|
|
<p><a href="http://weitz.de/index.html">BACK TO MY HOMEPAGE</a>
|
|
|
|
</body>
|
|
</html>
|