Currently, my job does not involve a whole lot of programming, and I’m getting a bit rusty and irritated. So, last weekend I decided to noodle around aimlessly with some systems I meant to try out for a while now.
Parenscript advertises itself as being easy to integrate with javascript libraries such as Prototype, but the manual gives no examples on how to do this. Furthermore, the Parenscript manual uses aserve / htmlgen for its examples, while much of the action these days is with hunchentoot. So, without further ado, a pointless example integrating hunchentoot, cl-who, parenscript and script.aculo.us. Load the code after loading hunchentoot, cl-who and parenscript into your running Lisp.
;;; Just the standard intro
(defpackage :js-hax
(:use :common-lisp :hunchentoot :cl-who :parenscript))
(in-package :js-hax)
(start-server :port 8080)
;;; Set the path to where you unpacked script.aculo.us
;;; create-folder-dispatcher-and-handler publishes subdirectories:
;;; http://.../js/lib/prototype.js
;;; http://.../js/src/scriptaculous.js
(push (create-folder-dispatcher-and-handler
"/js/" #p"/home/someone/source/scriptaculous-js-1.8.0/")
*dispatch-table*)
(defmacro js-script (&rest body)
"Utility macro for including ParenScript into the HTML
notation of cl-who"
`(with-html-output (*standard-output*)
(:script :type "text/javascript"
(format t "~%// <!--[CDATA[~%~A~%//]]-->~%" (ps ,@body)))))
;;; Slider example, taken from the scriptaculous wiki
(defun foo ()
(with-html-output-to-string (*standard-output* nil :prologue t)
(:html
(:head (:title "script.aculo.us Tests")
(:script :language "JavaScript" :type "text/javascript"
:src "/js/lib/prototype.js" "stupid firefox")
(:script :language "JavaScript" :type "text/javascript"
:src "/js/src/scriptaculous.js" "stupid firefox"))
(:body
(:h1 "script.aculo.us vs. ParenScript")
(:p "The humble slider.")
(:p
(:div :id "track1"
:style "width:200px;background-color:#aaa;height:5px;"
(:div
:id "handle1"
:style "width:5px;height:10px;background-color:#f00;cursor:move;"))
(:div :id "debug1" :style "padding-top: 5px;"))
(js-script
;; Create the Slider object
(new (*control.*slider
;; Hook it up to the UI elements ...
"handle1" "track1"
;; ... and assign behavior
(create on-slide (lambda (v)
;; Prototype-style DOM accessors:
;; $('debug1').innerHTML = ...
(setf (slot-value ($ "debug1")
'inner-h-t-m-l)
(+ "slide: " v)))
on-change (lambda (v)
(setf (slot-value ($ "debug1")
'inner-h-t-m-l)
(+ "changed! " v)))))))))))
(push (create-prefix-dispatcher "/foo" 'foo) *dispatch-table*)
All in all quite painless. The only point where I had to sit and think a bit was the translation of the prototype-style DOM accessors to parenscript notation.
Edit: Oh, and the js-script code above is missing the CDATA bits which were eaten by the web. Check page 6 of the parenscript documentation for the missing pieces.
Edit2: Thanks to Aankhen for pointing out that the CDATA wasn’t et after all, just hiding. I’ll go wrestle with this blog now to fix the stupid smartquotes.
Can’t say I’m a big fan of Prototype or script.aculo.us myself, but
interesting entry nonetheless as a practical demonstration of
integrating ParenScript. I have a few comments.
It would be nice if you could set up your blog software to not convert
quotes within code into smart quotes. The conversion makes a
copy/paste job impossible without a heck of a lot of tweaking.
What is the “stupid firefox” text in both `script’ elements supposed
to be? Judging by its position, I’d guess it’s placed as a child; why
is it in there? Text children of `script’ elements are never
displayed, only executed.
You don’t need both `language’ and `type’ attributes on your `script’
elements. `type=”text/javascript”‘ is all that is required.
Why does your `js-script’ macro start the script block with “// ” on a
line of its own? There’s nothing wrong with it, it’s just
unnecessary.
The CDATA isn’t “swallowed up by the web”. It’s present in the page
(see the source), but since it apparently wasn’t escaped it for
presentation in HTML, it was instead intepreted as a part thereof.
BTW, I noticed that your page uses a DOCTYPE of XHTML 1.0 Strict. You
might want to change that to something more lax, such as HTML 4.01
Loose, since your blog isn’t quite in line with what XHTML 1.0 Strict
requires
.
Hi, thanks for the comments!
cl-who renders tags without content as a single , not as a pair of begin/end tags. I found out through experimentation that firefox barfed in that case and would render an empty page. I considered reading up on / tweaking the DOCTYPE declaration since it might be within its rights to do so, but went for the quick hack instead.
the “type=” came from the scriptaculous demos, the “language=” from the parenscript documentation. You may now conclude that I’m not a very advanced web coding monkey.
The js-script macro was adapted from the parenscript docs (see relevant comment above about my web-fu).
Glad to be of service.
“cl-who renders tags without content as a single , not as a pair of begin/end tags. I found out through experimentation that firefox barfed in that case and would render an empty page.”
Ah yes, you have a point there. Strange to say, though, I had the opposite experience when I once tried an empty `script’ element: IE barfed while Firefox handled it alright.
“I considered reading up on / tweaking the DOCTYPE declaration since it might be within its rights to do so, but went for the quick hack instead.”
It’s not the DOCTYPE in this case. Browsers treat all HTML as tag soup (unless it’s sent as `application/xhtml+xml’); when you say , their safest bet is to assume that you actually meant (i.e. just the opening tag).
“the “type=” came from the scriptaculous demos, the “language=” from the parenscript documentation. You may now conclude that I’m not a very advanced web coding monkey.”
Heh, fair enough.
“The js-script macro was adapted from the parenscript docs (see relevant comment above about my web-fu).”
I would yell at you for cargo-culting if I weren’t guilty myself of doing the same thing on a regular basis.
Thanks for fixing the quotes and the escaping, it’s much more usable now!
Hmm… quite interesting. I think, I shall try to use Parenscript and Yahoo UI toolkit together to see if the match as neatly as script.aculo.us in the example.
Thanks for a nice tip!
Arghh, sorry for the documentation! I’ve just moved to California last week and with all the running around trying to find an apartment and getting everything figured out I haven’t had time to touch Parenscript at all. One of the things I promised a few months ago was to rewrite the tutorial file to use Hunchentoot and cl-who, and post it as a wiki page, ostensibly so people can easily contribute great examples like the above, but really because I am too lazy to maintain that much documentation. I’ll put that on the list of things to do before 2007 is out.
No no, the documentation is very nice, and well-structured! If you wikify it, please consider keeping the official docs as well. In fact, I volunteer to convert the manual to hunchentoot / cl-who.
Regarding the single tags, see here:
http://weitz.de/cl-who/#*html-empty-tags*
many thanks for this blog entry; i’m new to all things lisp (well, new in that is has been like 20 years since i last used it for my day job) and posts like this are a great help in getting my bearings. i want to learn enough to be able to cobble together weekend AJAXy hacks.
Thanks for your original posting. I borrowed the code and managed to set up a timed slideshow with it, with new list items being inserted into the head of a list and the oldest photo fading from the end of the list. This was a great help, thank you.
My pleasure, Ron! Glad I could help.