Araneida Reference : HTML generation

# 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!