HTML Generation

We have functions that turn sexps into HTML. The format of the sexp is

((element-name {:attribute-name attribute-value}*) {contents})
where element-name is a symbol, attribute-name is a keyword, and contents are more elements, or strings. If there are no attributes, the parens around element-name can be omitted. For example
(html
 (head (title "Title"))
 (body (p "Click here to visit ((a :href "http://www.google.com/") "Google"))))

There is a special case for a tag named comment.

(html
 (comment (span "yes")))

=>

<!--
<span>yes</span> -->

You can also do:

(html
 ((comment "This is no longer used") (span "yes")))

=>

<!-- This is no longer used
<span>yes</span> -->

The functions are HTML (returns a string) and HTML-STREAM (outputs directly to a stream). The latter is much less consy, so to be preferred

We also have a pattern-based rewriting system so that you can "invent your own tags" (sic), using DEFINE-PATTERNS. See example 6 in ../examples/main.lisp. This is used in the DEFINE-PAGE macro, which also gives you correct handling of conditional GETs for free.

Custom HTML tags

They're kinda like macros

You can create custom tags that do "special" things.

(defhtmltag coffee (attr content)
  (declare (ignore attr content))
  "c|_|")

Let's you do:

(html (span "Coffee: " (coffee)))
=>
<span>Coffee c|_|</span>

That's a very simplistic example, of course.

A more in-depth example might be:

(defhtmltag odd-even-list (attr content)
  `((ul ,@attr)
    ,@(let ((counter 0))
	(mapcar (lambda (elt)
		  (incf counter)
		  (destructure-html (tag attrs content) elt
		    `((,tag :class ,(if (oddp counter) "odd" "even") ,@attrs) ,@content)))
		content))))
(html
  (odd-even-list
    (li "odd")
    (li "even")
    (li "odd")
    (li "even")))

=>

<ul>
   <li class="odd">odd</li>
   <li class="even">even</li>
   <li class="odd">odd</li>
   <li class="even">even</li>
</ul>

When writing more advanced custom tags, you may find DESTRUCTURE-HTML quite useful (see above for sample code).

Templates

A template is nothing special, it's just a function with a funny name. It can be useful, though, for keeping your templates out of the rest of your code, and for grepping purposes.

(deftemplate mytemplate (&optional warning)
  `(p \"A regular paragraph\" ,@(when warning (blink \"NOW WITH A BADLY FORMATTED WARNING!\"))))

; it's called:
(html
  (call-template 'mytemplate))

Templates do not use the function namespace, so you couldn't do, for instance: (mytemplate)

You can trace and untrace templates, as well:

(trace-template mytemplate)
(untrace-template mytemplate)

Integration with Parenscript

If you load Parenscript before Araneida, you can use it with your normal HTML generation routines.

For example:

(html '((span :css (:color "black" :size "200%")) "Print this"))

Would result in:

<span style="color:black;size:200%">Print this</span>

If you need a block of CSS, you can do:

(html  '(html
	  (head (title "A simple title")
		(css (h1 :color "red")
		     (h2 :color "blue")))
	  (body (h1 "A simple header"))))

To get:

<html><head><title>A simple title</title>
<style type="text/css">
<!--
h1 {
   color:red;
}

h2 {
   color:blue;
}


--></style></head>
<body><h1>A simple header</h1>
</body>
</html>

If you need to send CSS as a file, just do:

(defmethod handle-request-response ((handler my-handler) method request)
  (css-file request
	    (* :border \"1px solid black\")
	    (div.bl0rg :font-family \"serif\")
	    ((\"a:active\" \"a:hoover\") :color \"black\" :size \"200%\")))

It will send it with the proper content type, even.

On the Javascript side of things, if you need inline Javascript, do:

(html `((a :onclick ,(js:js-inline (alert "click!"))) "Click me!"))

To get:

<a onclick="javascript:alert('click!');">Click me!</a>

If you need a block of Javascript code, do:

(html `(html
		     (head (title "Another simple title")
		      (js-script
		       (defun foo (x)
			 (alert (+ "Be a lert: " x)))
		       (defun bar (x)
			 (alert (+ "Bar - " x)))))
		     (body
		      (p "All foos and bars come to an end")
		      (p ((a :onclick ,(js:js-inline (foo "end"))) "Foo!"))
		      (p ((a :onclick ,(js:js-inline (bar "end"))) "Bar!")))))

And the Javascript will be put in a proper <script> block.

Lastly, if you need to send a javascript file, it's exactly like css-file, except it's called js-file.

Remember to use Araneida's css-file and js-file and NOT Parenscript's.

On the same note, please note that Parenscript's HTML generation code is called within Parenscript code and NOT Araneida's. This means that you would say

(js-script
  (defun add-div (name href link-text)
    (document.write
     (html ((:div :id name)
            "The link is: "
            ((:a :href href) link-text))))))

Just like in the Parenscript docs.

Enjoy!