From: Vladimir Sedach Date: Thu, 12 Mar 2009 08:04:06 +0000 (-0600) Subject: Removed tutorial.lisp from repository (it is now updated and placed online in HTML... X-Git-Url: https://git.hcoop.net/clinton/parenscript.git/commitdiff_plain/19719c462494052b526338f9c241ac4cfa65750d Removed tutorial.lisp from repository (it is now updated and placed online in HTML format: common-lisp.net/project/parenscript/tutorial.html) --- diff --git a/docs/build.sh b/docs/build.sh index d9569fa..734fd4b 100644 --- a/docs/build.sh +++ b/docs/build.sh @@ -1 +1 @@ -python pbook.py -o manual.pdf introduction.lisp tutorial.lisp reference.lisp \ No newline at end of file +python pbook.py -o manual.pdf introduction.lisp reference.lisp \ No newline at end of file diff --git a/docs/tutorial.lisp b/docs/tutorial.lisp deleted file mode 100644 index 67e1509..0000000 --- a/docs/tutorial.lisp +++ /dev/null @@ -1,578 +0,0 @@ -;;;# ParenScript Tutorial - -;;; This chapter is a short introductory tutorial to ParenScript. It -;;; hopefully will give you an idea how ParenScript can be used in a -;;; Lisp web application. - -;;;# Setting up the ParenScript environment - -;;; In this tutorial, we will use the Portable Allegroserve webserver -;;; to serve the tutorial web application. We use the ASDF system to -;;; load both Allegroserve and ParenScript. I assume you have -;;; installed and downloaded Allegroserve and Parenscript, and know -;;; how to setup the central registry for ASDF. - -(asdf:oos 'asdf:load-op :aserve) - -; ... lots of compiler output ... - -(asdf:oos 'asdf:load-op :parenscript) - -; ... lots of compiler output ... - -;;; The tutorial will be placed in its own package, which we first -;;; have to define. - -(defpackage :js-tutorial - (:use :common-lisp :net.aserve :net.html.generator :parenscript)) - -(in-package :js-tutorial) - -;;; The next command starts the webserver on the port 8080. - -(start :port 8080) - -;;; We are now ready to generate the first JavaScript-enabled webpages -;;; using ParenScript. - -;;;# A simple embedded example - -;;; The first document we will generate is a simple HTML document, -;;; which features a single hyperlink. When clicking the hyperlink, a -;;; JavaScript handler opens a popup alert window with the string -;;; "Hello world". To facilitate the development, we will factor out -;;; the HTML generation to a separate function, and setup a handler -;;; for the url "/tutorial1", which will generate HTTP headers and -;;; call the function `TUTORIAL1'. At first, our function does nothing. - -(defun tutorial1 (req ent) - (declare (ignore req ent)) - nil) - -(publish :path "/tutorial1" - :content-type "text/html; charset=ISO-8859-1" - :function (lambda (req ent) - (with-http-response (req ent) - (with-http-body (req ent) - (tutorial1 req ent))))) - -;;; Browsing "http://localhost:8080/tutorial1" should return an empty -;;; HTML page. It's now time to fill this rather page with -;;; content. ParenScript features a macro that generates a string that -;;; can be used as an attribute value of HTML nodes. - -(defun tutorial1 (req ent) - (declare (ignore req ent)) - (html - (:html - (:head (:title "ParenScript tutorial: 1st example")) - (:body (:h1 "ParenScript tutorial: 1st example") - (:p "Please click the link below." :br - ((:a :href "#" :onclick (ps-inline - (alert "Hello World"))) - "Hello World")))))) - -;;; Browsing "http://localhost:8080/tutorial1" should return the -;;; following HTML: - -ParenScript tutorial: 1st example - -

ParenScript tutorial: 1st example

-

Please click the link below.
-Hello World -

- - - -;;;# Adding an inline ParenScript - -;;; Suppose we now want to have a general greeting function. One way -;;; to do this is to add the javascript in a `SCRIPT' element at the -;;; top of the HTML page. This is done using the `JS-SCRIPT' macro -;;; (defined below) which will generate the necessary XML and comment -;;; tricks to cleanly embed JavaScript. We will redefine our -;;; `TUTORIAL1' function and add a few links: - -(defmacro js-script (&rest body) - "Utility macro for including ParenScript into the HTML notation -of net.html.generator library that comes with AllegroServe." - `((:script :type "text/javascript") - (:princ (format nil "~%// ~%")))) - -(defun tutorial1 (req ent) - (declare (ignore req ent)) - (html - (:html - (:head - (:title "ParenScript tutorial: 2nd example") - (js-script - (defun greeting-callback () - (alert "Hello World")))) - (:body - (:h1 "ParenScript tutorial: 2nd example") - (:p "Please click the link below." :br - ((:a :href "#" :onclick (ps-inline (greeting-callback))) - "Hello World") - :br "And maybe this link too." :br - ((:a :href "#" :onclick (ps-inline (greeting-callback))) - "Knock knock") - :br "And finally a third link." :br - ((:a :href "#" :onclick (ps-inline (greeting-callback))) - "Hello there")))))) - -;;; This will generate the following HTML page, with the embedded -;;; JavaScript nicely sitting on top. Take note how -;;; `GREETING-CALLBACK' was converted to camelcase, and how the lispy -;;; `DEFUN' was converted to a JavaScript function declaration. - -ParenScript tutorial: 2nd example - - -

ParenScript tutorial: 2nd example

-

Please click the link below.
-Hello World -
-And maybe this link too.
-Knock knock -
- -And finally a third link.
-Hello there -

- - - -;;;# Generating a JavaScript file - -;;; The best way to integrate ParenScript into a Lisp application is -;;; to generate a JavaScript file from ParenScript code. This file can -;;; be cached by intermediate proxies, and webbrowsers won't have to -;;; reload the JavaScript code on each pageview. We will publish the -;;; tutorial JavaScript under "/tutorial.js". - -(defun tutorial1-file (req ent) - (declare (ignore req ent)) - (html (:princ - (ps (defun greeting-callback () - (alert "Hello World")))))) - -(publish :path "/tutorial1.js" - :content-type "text/javascript; charset=ISO-8859-1" - :function (lambda (req ent) - (with-http-response (req ent) - (with-http-body (req ent) - (tutorial1-file req ent))))) - -(defun tutorial1 (req ent) - (declare (ignore req ent)) - (html - (:html - (:head - (:title "ParenScript tutorial: 3rd example") - ((:script :language "JavaScript" :src "/tutorial1.js"))) - (:body - (:h1 "ParenScript tutorial: 3rd example") - (:p "Please click the link below." :br - ((:a :href "#" :onclick (ps-inline (greeting-callback))) - "Hello World") - :br "And maybe this link too." :br - ((:a :href "#" :onclick (ps-inline (greeting-callback))) - "Knock knock") - :br "And finally a third link." :br - ((:a :href "#" :onclick (ps-inline (greeting-callback))) - "Hello there")))))) - -;;; This will generate the following JavaScript code under -;;; "/tutorial1.js": - -function greetingCallback() { - alert("Hello World"); -} - -;;; and the following HTML code: - -ParenScript tutorial: 3rd example - - -

ParenScript tutorial: 3rd example

-

Please click the link below.
-Hello World -
-And maybe this link too.
-Knock knock -
- -And finally a third link.
-Hello there -

- - - -;;;# A ParenScript slideshow - -;;; While developing ParenScript, I used JavaScript programs from the -;;; web and rewrote them using ParenScript. This is a nice slideshow -;;; example from - - http://www.dynamicdrive.com/dynamicindex14/dhtmlslide.htm - -;;; The slideshow will be accessible under "/slideshow", and will -;;; slide through the images "photo1.png", "photo2.png" and -;;; "photo3.png". The first ParenScript version will be very similar -;;; to the original JavaScript code. The second version will then show -;;; how to integrate data from the Lisp environment into the -;;; ParenScript code, allowing us to customize the slideshow -;;; application by supplying a list of image names. We first setup the -;;; slideshow path. - -(publish :path "/slideshow" - :content-type "text/html" - :function (lambda (req ent) - (with-http-response (req ent) - (with-http-body (req ent) - (slideshow req ent))))) - -(publish :path "/slideshow.js" - :content-type "text/html" - :function (lambda (req ent) - (with-http-response (req ent) - (with-http-body (req ent) - (js-slideshow req ent))))) - -;;; The images are just random files I found on my harddrive. We will -;;; publish them by hand for now. - -(publish-file :path "/photo1.jpg" - :file "/home/viper/photo1.jpg") -(publish-file :path "/photo2.jpg" - :file "/home/viper/photo2.jpg") -(publish-file :path "/photo3.jpg" - :file "/home/viper/photo3.jpg") - -;;; The function `SLIDESHOW' generates the HTML code for the main -;;; slideshow page. It also features little bits of ParenScript. These -;;; are the callbacks on the links for the slideshow application. In -;;; this special case, the javascript generates the links itself by -;;; using `document.write' in a "SCRIPT" element. Users that don't -;;; have JavaScript enabled won't see anything at all. -;;; -;;; `SLIDESHOW' also generates a static array called `PHOTOS' which -;;; holds the links to the photos of the slideshow. This array is -;;; handled by the ParenScript code in "slideshow.js". Note how the -;;; HTML code issued by ParenScrip is generated using the `PS-HTML' -;;; construct. In fact, there are two different HTML generators in the -;;; example below, one is the AllegroServe HTML generator, and the -;;; other is the ParenScript standard library HTML generator, which -;;; produces a JavaScript expression which evaluates to an HTML -;;; string. - -(defun slideshow (req ent) - (declare (ignore req ent)) - (html - (:html - (:head (:title "ParenScript slideshow") - ((:script :language "JavaScript" - :src "/slideshow.js")) - (js-script - (defvar *linkornot* 0) - (defvar photos (array "photo1.jpg" - "photo2.jpg" - "photo3.jpg")))) - (:body (:h1 "ParenScript slideshow") - (:body (:h2 "Hello") - ((:table :border 0 - :cellspacing 0 - :cellpadding 0) - (:tr ((:td :width "100%" :colspan 2 :height 22) - (:center - (js-script - (let ((img (ps-html - ((:img :src (aref photos 0) - :name "photoslider" - :style (+ "filter:" - (lisp (ps (reveal-trans - (setf duration 2) - (setf transition 23))))) - :border 0))))) - (document.write - (if (= *linkornot* 1) - (ps-html ((:a :href "#" - :onclick (lisp (ps-inline (transport)))) - img)) - img))))))) - (:tr ((:td :width "50%" :height "21") - ((:p :align "left") - ((:a :href "#" - :onclick (ps-inline (backward) - (return false))) - "Previous Slide"))) - ((:td :width "50%" :height "21") - ((:p :align "right") - ((:a :href "#" - :onclick (ps-inline (forward) - (return false))) - "Next Slide")))))))))) - -;;; `SLIDESHOW' generates the following HTML code (long lines have -;;; been broken): - -ParenScript slideshow - - - -

ParenScript slideshow

-

Hello

- - - - - - -
-
-
-

-Previous Slide - -

-

-Next Slide -

-
- - - - -;;; The actual slideshow application is generated by the function -;;; `JS-SLIDESHOW', which generates a ParenScript file. Symbols are -;;; converted to JavaScript variables, but the dot "." is left as -;;; is. This enables convenient access to object slots without using -;;; the `SLOT-VALUE' function all the time. However, when the object -;;; we are referring to is not a variable, but for example an element -;;; of an array, we have to revert to `SLOT-VALUE'. - -(defun js-slideshow (req ent) - (declare (ignore req ent)) - (html - (:princ - (ps - (defvar *preloaded-images* (make-array)) - (defun preload-images (photos) - (dotimes (i photos.length) - (setf (aref *preloaded-images* i) (new *Image) - (slot-value (aref *preloaded-images* i) 'src) - (aref photos i)))) - - (defun apply-effect () - (when (and document.all photoslider.filters) - (let ((trans photoslider.filters.reveal-trans)) - (setf (slot-value trans '*Transition) - (floor (* (random) 23))) - (trans.stop) - (trans.apply)))) - - (defun play-effect () - (when (and document.all photoslider.filters) - (photoslider.filters.reveal-trans.play))) - - (defvar *which* 0) - - (defun keep-track () - (setf window.status - (+ "Image " (1+ *which*) " of " photos.length))) - - (defun backward () - (when (> *which* 0) - (decf *which*) - (apply-effect) - (setf document.images.photoslider.src - (aref photos *which*)) - (play-effect) - (keep-track))) - - (defun forward () - (when (< *which* (1- photos.length)) - (incf *which*) - (apply-effect) - (setf document.images.photoslider.src - (aref photos *which*)) - (play-effect) - (keep-track))) - - (defun transport () - (setf window.location (aref photoslink *which*))))))) - -;;; `JS-SLIDESHOW' generates the following JavaScript code: - -var PRELOADEDIMAGES = new Array(); -function preloadImages(photos) { - for (var i = 0; i != photos.length; i = i++) { - PRELOADEDIMAGES[i] = new Image; - PRELOADEDIMAGES[i].src = photos[i]; - } -} -function applyEffect() { - if (document.all && photoslider.filters) { - var trans = photoslider.filters.revealTrans; - trans.Transition = Math.floor(Math.random() * 23); - trans.stop(); - trans.apply(); - } -} -function playEffect() { - if (document.all && photoslider.filters) { - photoslider.filters.revealTrans.play(); - } -} -var WHICH = 0; -function keepTrack() { - window.status = "Image " + (WHICH + 1) + " of " + - photos.length; -} -function backward() { - if (WHICH > 0) { - --WHICH; - applyEffect(); - document.images.photoslider.src = photos[WHICH]; - playEffect(); - keepTrack(); - } -} -function forward() { - if (WHICH < photos.length - 1) { - ++WHICH; - applyEffect(); - document.images.photoslider.src = photos[WHICH]; - playEffect(); - keepTrack(); - } -} -function transport() { - window.location = photoslink[WHICH]; -} - -;;;# Customizing the slideshow - -;;; For now, the slideshow has the path to all the slideshow images -;;; hardcoded in the HTML code, as well as in the publish -;;; statements. We now want to customize this by publishing a -;;; slideshow under a certain path, and giving it a list of image urls -;;; and pathnames where those images can be found. For this, we will -;;; create a function `PUBLISH-SLIDESHOW' which takes a prefix as -;;; argument, as well as a list of image pathnames to be published. - -(defun publish-slideshow (prefix images) - (let* ((js-url (format nil "~Aslideshow.js" prefix)) - (html-url (format nil "~Aslideshow" prefix)) - (image-urls - (mapcar (lambda (image) - (format nil "~A~A.~A" prefix - (pathname-name image) - (pathname-type image))) - images))) - (publish :path html-url - :content-type "text/html" - :function (lambda (req ent) - (with-http-response (req ent) - (with-http-body (req ent) - (slideshow2 req ent image-urls))))) - (publish :path js-url - :content-type "text/html" - :function (lambda (req ent) - (with-http-response (req ent) - (with-http-body (req ent) - (js-slideshow req ent))))) - (map nil (lambda (image url) - (publish-file :path url - :file image)) - images image-urls))) - -(defun slideshow2 (req ent image-urls) - (declare (ignore req ent)) - (html - (:html - (:head (:title "ParenScript slideshow") - ((:script :language "JavaScript" - :src "/slideshow.js")) - ((:script :type "text/javascript") - (:princ (format nil "~%// ~%")))) - (:body (:h1 "ParenScript slideshow") - (:body (:h2 "Hello") - ((:table :border 0 - :cellspacing 0 - :cellpadding 0) - (:tr ((:td :width "100%" :colspan 2 :height 22) - (:center - (js-script - (let ((img (ps-html - ((:img :src (aref photos 0) - :name "photoslider" - :style (+ "filter:" - (lisp (ps (reveal-trans - (setf duration 2) - (setf transition 23))))) - :border 0))))) - (document.write - (if (= *linkornot* 1) - (ps-html ((:a :href "#" - :onclick (lisp (ps-inline (transport)))) - img)) - img))))))) - (:tr ((:td :width "50%" :height "21") - ((:p :align "left") - ((:a :href "#" - :onclick (ps-inline (backward) - (return false))) - "Previous Slide"))) - ((:td :width "50%" :height "21") - ((:p :align "right") - ((:a :href "#" - :onclick (ps-inline (forward) - (return false))) - "Next Slide")))))))))) - -;;; We can now publish the same slideshow as before, under the -;;; "/bknr/" prefix: - -(publish-slideshow "/bknr/" - `("/home/viper/photo1.jpg" "/home/viper/photo2.jpg" "/home/viper/photo3.jpg")) - -;;; That's it, we can now access our customized slideshow under - - http://localhost:8080/bknr/slideshow -