/[ht-ajax]/doc/ht-ajax.html
ViewVC logotype

Contents of /doc/ht-ajax.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Fri Nov 14 21:17:43 2008 UTC (5 years, 5 months ago) by xlopez
File MIME type: text/html
File size: 22270 byte(s)
Initial commit, version 0.0.7.
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml">
3 <!--
4 ;;;
5 ;;; Copyright (c) 2007, Ury Marshak
6 ;;; The code comes with a BSD-style license, so you can basically do
7 ;;; with it whatever you want. See the file LICENSE for details.
8 ;;;
9 -->
10 <head>
11 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
12 <title>HT-AJAX - Common Lisp AJAX framework for Hunchentoot</title>
13 <meta name="description" content="HT-AJAX is an AJAX framework in Common Lisp for Hunchentoot web server under BSD license, portable, is known to run on SBCL and Lispworks. Supports such Javascript libraries as Lokris, Prototype, YUI and Dojo. Supports JSON, virtual Javascript files,Javascript minimization." />
14 <meta name="keywords" content="HT-AJAX,AJAX,Common Lisp,Lisp,Hunchentoot,framework,opensource,BSD license,library,SBCL,Lispworks,Lokris,Prototype,Dojo,YUI,JSON, virtual Javascript files, Javascript minimization" />
15
16 <style type="text/css">
17 pre { padding:5px; background-color:#e0e0e0;
18 margin-left: 1cm; margin-right: 3cm;
19 margin-top: 0cm; margin-bottom: 0cm;
20 }
21 h3, h4 { text-decoration: underline; }
22 a { text-decoration: none; }
23 a.noborder { border:0px }
24 a.noborder:hover { border:0px } a.none { border:1px solid white; }
25 a.none { border:1px solid white; }
26 a.none:hover { border:1px solid white; }
27
28 a.note { border:1px solid white; text-decoration: bold; }
29
30 a { border:1px solid white; }
31 a:hover { border: 1px solid black; }
32 a.noborder { border:0px }
33 a.noborder:hover { border:0px }
34 .browser { background-color:#f0f0f0; border: 1px solid black;
35 margin-left: 1cm; margin-right: 3cm;
36 margin-top: 0cm; margin-bottom: 0cm;
37 }
38 </style>
39 </head>
40
41 <body bgcolor="white">
42
43 <h2> HT-AJAX - AJAX framework for Hunchentoot</h2>
44
45 <div style="margin-left: 4cm; margin-right: 4cm;">
46 <br />&nbsp;<br /><h3><a name="abstract" class="none">Abstract</a></h3>
47
48 <p>
49 HT-AJAX is a small <a href="http://www.lisp.org">Common Lisp</a> framework that is designed to ease dynamic
50 interaction of your web pages with the server. It runs under the
51 <a href="http://weitz.de/hunchentoot/">Hunchentoot</a> web server by Dr. Edi Weitz.
52 </p>
53
54 <p>
55 Basically it allows 'exporting' of your lisp functions so that they can be easily called
56 from the Javascript code on your web page. The details of the data transfer can be
57 handled by different backends, or as we'll call them 'AJAX processors'. At the moment three
58 such processors are supported: one simple, built-in, that generates code inside the
59 web page and does not require external libraries. The others are using a javascript
60 <a href="#supported-libraries">library</a>, such as
61 <a href="http://prototypejs.org/">Prototype</a> or
62 <a href="http://dojotoolkit.org/">Dojo</a>
63 <a href="#supported-libraries">(full list)</a>.
64 </p>
65
66 <p>
67 The code comes with
68 a <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
69 license</a> so you can basically do with it whatever you want.
70 </p>
71
72 <p>
73 <font color="red">Download shortcut:</font> <a href="http://85.65.214.241/misc/ht-ajax.tar.gz">http://85.65.214.241/misc/ht-ajax.tar.gz</a>.
74 </p>
75 </div>
76
77 <br />&nbsp;<br /><h3><a class="none" name="contents">Contents</a></h3>
78 <ol>
79 <li><a href="#download">Download and installation</a></li>
80 <li><a href="#support">Support</a></li>
81 <li><a href="#getting-started">Getting started (mini-tutorial)</a></li>
82 <li><a href="#choosing-processor">Choosing the right ajax-processor</a></li>
83 <li><a href="#generated-js">Using the generated Javascript functions</a></li>
84 <li><a href="#dictionary">The HT-AJAX dictionary</a>
85 <ol>
86 <li><a href="#make-ajax-processor"><code>make-ajax-processor</code></a></li>
87 <li><a href="#export-func"><code>export-func</code></a></li>
88 <li><a href="#unexport-func"><code>unexport-func</code></a></li>
89 <li><a href="#defun-ajax"><code>defun-ajax</code></a></li>
90 <li><a href="#generate-prologue"><code>generate-prologue</code></a></li>
91 <li><a href="#get-handler"><code>get-handler</code></a></li>
92 </ol>
93 </li>
94 <li><a href="#supported-libraries">Supported Javascript libraries</a></li>
95 <li><a href="#portability">Portability</a></li>
96 <li><a href="#notes">Notes</a></li>
97 <li><a href="#ack">Acknowledgements</a></li>
98 </ol>
99
100 <br />&nbsp;<br /><h3><a class="none" name="download">Download and installation</a></h3>
101
102 HT-AJAX together with this documentation can be downloaded from
103 <a href="http://85.65.214.241/misc/ht-ajax.tar.gz">http://85.65.214.241/misc/ht-ajax.tar.gz</a>.
104 The current version is 0.0.7 .
105
106 <br />
107 If you have <a href="http://www.cliki.net/ASDF-Install">ASDF-Install</a> working you can use
108 it to install HT-AJAX:
109 <pre>
110 (require 'asdf-install)
111 (asdf-install:install :ht-ajax)
112 </pre>
113
114 <br />
115 Otherwise, download and untar the distribution and use the normal procedure for your system to
116 install a library that comes with an ASDF system definition. For example, make an appropriate
117 symbolic link to the ht-ajax.asd file and run
118 <pre>(require 'asdf)
119 (asdf:oos 'asdf:load-op :ht-ajax)</pre>
120
121 <br />&nbsp;<br /><h3><a class="none" name="support">Support</a></h3>
122 <p>Questions, bug reports, requests, criticism and just plain information that you found
123 this package useful (or useless) are to be sent to
124 <span>ur</span>ym<span>@t</span>wo-by<span>te</span>s<span>.</span>c<span>o</span>m
125
126 </p>
127
128 <br />&nbsp;<br /><h3><a class="none" name="getting-started">Getting started (mini-tutorial)</a></h3>
129 <p>In this tutorial we assume that the reader is reasonably familiar with Hunchentoot
130 and AJAX.</p>
131
132
133 <p>So, let's suppose we already have some lisp code working under Hunchentoot and start
134 modifying it to use AJAX.
135 Note that normally we'll <b>(use-package :ht-ajax)</b>, so that we won't have to prefix
136 the symbols with <b>ht-ajax:</b>, but here we'll still do it to show clearly which symbols
137 come from the HT-AJAX package.</p>
138
139
140 <p>At first some setup:</p>
141 <pre>
142 (defparameter *ajax-handler-url* "/hunchentoot/some/suitable/uri/ajax-hdlr")
143 </pre>
144 <p>Here we select some URL that will handle our AJAX requests. Later we'll need to arrange
145 for an appropriate handler to be called when we get a request for this URL (and all
146 URLs starting with it). Replace the URL with whatever makes sense for your application</p>
147
148 <br /> <br />
149 <p>After this we create an instance of so-called ajax-processor that will handle all our
150 AJAX needs. One ajax-processor per application should be enough. We pass the following
151 parameters: <b>:type :lokris</b> to select which backend to use, in this case it's
152 the <a href="http://www.ajax-buch.de/lokris/">Lokris</a> library. Also we pass the
153 <b>:server-uri</b> that we've selected and the <b>:js-file-uris</b> that shows where
154 to find the appropriate library file, lokris.js in this case (the URL may be relative
155 to the URL of the page):</p>
156 <pre>
157 (defparameter *ajax-processor* (ht-ajax:make-ajax-processor
158 :type :lokris
159 :server-uri *ajax-handler-url*
160 :js-file-uris "static/lokris.js"))
161 </pre>
162
163 <br />
164
165 <p>Now we create the function that we want to be called from the web page:</p>
166 <pre>
167 (ht-ajax:defun-ajax testfunc (command) (*ajax-processor* :method :post)
168 (prin1-to-string (eval (read-from-string command nil))))
169 </pre>
170 <p>We've used here the <b>defun-ajax</b> macro that performs two tasks: defines the function
171 as per <b>defun</b> and 'exports' it - makes available to the web page's javascript.
172 This fragment could've been written more verbosely as:</p>
173 <pre>
174 (defun testfunc (command)
175 (prin1-to-string (eval (read-from-string command nil))))
176
177 (ht-ajax:export-func *ajax-processor* 'testfunc :method :post)
178 </pre>
179 <p>The function itself contains the code to evaluate the string parameter <i>command</i>
180 and return the result as a string. (It's possible to return more complex objects to the
181 Javascript code by using <a href="http://www.json.org/">JSON</a>).
182 While processig the request HT-AJAX will call Hunchentoot's function
183 <a href="http://weitz.de/hunchentoot/#no-cache"><b>no-cache</b></a> to make sure the browser
184 will make a request to the server each time and not cache the results, so we don't have to
185 do it here. If we want to manually control the caching we can pass <b>:allow-cache t</b>
186 parameter when exporting the function.
187 </p>
188
189 <p>The only thing left to prepare the server side of the things
190 is to create the dispatcher for our <b>*ajax-handler-url*</b> and to add it to
191 Hunchentoot's dispatch table. The specifics of this can vary, but it might include something
192 like:
193 </p>
194 <pre>(create-prefix-dispatcher *ajax-handler-url* (ht-ajax:get-handler *ajax-processor*)
195 </pre>
196 <p> The call to <b>(ht-ajax:get-handler *ajax-processor*)</b> returns the handler that the
197 handler URL
198 needs to be dispatched to.<a href="#note1"><sup>[1]</sup></a></p></p>
199 <br />
200 <p>Now we need to make sure that the dynamic web pages we generate are correctly set up.
201 This means that the result of the call <b>(ht-ajax:generate-prologue *ajax-processor*)</b>
202 needs to be inserted somewhere in the HTML page (right after the <b>&lt;body&gt;</b> tag
203 seems like
204 a good place). Once again how to do this depends on the facilities that are used for HTML
205 generation.
206 For example when using <a href="http://weitz.de/html-template/">HTML-TEMPLATE</a> we'll have
207 something like the following in our template:</p>
208 <pre>
209 &lt;body&gt;
210 &lt;!-- TMPL_VAR prologue --&gt;
211 </pre>
212 <p>and then pass the output of <b>(ht-ajax:get-handler *ajax-processor*)</b> as the
213 <i>prologue</i> parameter to the <b>fill-and-print-template</b> call.<a href="#note2"><sup>[2]</sup></a></p>
214
215 <p>After that, whatever means for HTML generation we're using, let's put the following HTML
216 somewhere in the page:</p>
217 <pre>
218
219 &lt;table width="50%"&gt;
220 &lt;tr&gt;
221 &lt;td colspan="2"&gt;
222 &lt;span id="result"&gt; &lt;i&gt;no results yet&lt;/i&gt;
223 &lt;/span&gt;
224 &lt;/td&gt;
225 &lt;/tr&gt;
226 &lt;tr&gt;
227 &lt;td width="70%"&gt;
228 &lt;input type="text" size="70" name="command" id="command" /&gt;
229 &lt;/td&gt;
230 &lt;td&gt;
231 &lt;input type="button" value="Eval" onclick="javascript:command_clicked();"/&gt;
232 &lt;/td&gt;
233 &lt;/tr&gt;
234 &lt;/table&gt;
235
236 </pre>
237
238 <p>This will produce something like:</p>
239 <div class="browser">
240 <table width="50%">
241 <tr>
242 <td colspan="2">
243 <span id="result"> <i>no results yet</i>
244 </span>
245 </td>
246 </tr>
247 <tr>
248 <td width="70%">
249 <input type="text" size="70" name="command" id="command" />
250 </td>
251 <td>
252 <input type="button" value="Eval" />
253 </td>
254 </tr>
255 </table>
256 </div>
257
258 <br />
259 Now write into the template the javascript function that will be called when you click the
260 button:
261 <pre>
262 &lt;script type="text/javascript"&gt;
263 function command_clicked(txt) {
264 // get the current value of the text input field
265 var command = document.getElementById('command').value;
266
267 // call function testfunc on the server with the parameter
268 // command and set the element with the id 'result' to the return
269 // value of the function
270 ajax_testfunc_set_element('result', command);
271 }
272 &lt;/script&gt;
273 </pre>
274 <p>The function <b>ajax_testfunc_set_element</b> that we call here was generated for us by
275 HT-AJAX.
276 It takes one required parameter - the id of the element that we want to be set to the result of
277 the remote call. All other parameters will be passed to corresponding exported lisp function,
278 <b>testfunc</b> in this case (watch out for the number of arguments). The resulting string will
279 be assigned to the <b>.innerHTML</b> property of the element with the id 'result'.
280 </p>
281
282 <br />
283
284 <p>This is it. Now to save the files, compile the lisp code, make sure the
285 Hunchentoot server is started and open the web page in the browser.
286 Enter something like <b>(+ 1 42)</b> in the text field and click 'Eval'.
287 If all's well the results of the evaluation will be displayed.</p>
288
289 <br />
290
291
292
293 <br />&nbsp;<br /><h3><a class="none" name="choosing-processor">Choosing the right ajax-processor</a></h3>
294 <p>If one of the <a href="#supported-libraries">supported libraries</a>
295 (like <a href="http://prototypejs.org/">Prototype</a>) is
296 already used in Javascript code then the decision is is easy - use the appropriate ajax-processor.
297 Otherwise consider if you need to make server calls using HTTP GET or POST method. If GET works
298 for you then SIMPLE may be enough, otherwise for POST method use Lokris.
299 </p>
300
301 <br />&nbsp;<br /><h3><a class="none" name="generated-js">Using the generated Javascript functions</a></h3>
302 <p>
303 Exporting the function (and later including the result of the GENERATE-PROLOGUE call in the
304 web page) makes available two functions for the Javascript code on the page. Assuming the
305 exported function was called TESTFUNC and the standard prefix (ajax_) was used they are:
306 </p>
307 <pre>
308 ajax_testfunc_callback(<i>callback_specification</i>, [params for the server's TESTFUNC....])
309 </pre>
310 and
311 <pre>
312 ajax_testfunc_set_element(element_id, [params for the server's TESTFUNC....])
313 </pre>
314 <p>
315 Both functions will call the server-side function TESTFUNC, the <b>ajax_testfunc_callback</b>
316 version will call the provided <i>callback</i> function with the result of the server call
317 as a single parameter, the <b>ajax_testfunc_set_element</b> version with find the document
318 element with the id <i>element_id</i> and set it's <b>innerHTML</b> to the result of the
319 server call.<br />
320 The result of the server call is normally a string and is passed to the callback as-is,
321 unless the Content-Type header was set to <b>application/json</b> which is the
322 <a href="http://www.iana.org/assignments/media-types/application/">official</a> IANA
323 media type. In case of JSON the result is evaluated using &quot;unsafe&quot;
324 <a href="#note3"><sup>[3]</sup></a> <b>eval</b>
325 call and the resulting object is passed to the callback.
326 <br />
327 </p>
328 <p><a class="none" name="callback-specification"></a>
329 The <i>callback_specification</i> parameter can be used
330 to specify two kinds of callacks (at the same time). The success callback function will
331 be called
332 after a successful interaction with the server and passed the server call result
333 as a parameter. The error callback function will be called in case of an error
334 and passed a string with the information about the error.
335 <br />So the <i>callback_specification</i> can take the following forms:</p>
336 <ul>
337 <li>A function. This will be used as a success callback.</li>
338 <li>An array of two functions. The first will be used as a success callback,
339 the second as an error callback.</li>
340 <li>An object. The value of the "success" property of the object
341 will be used as a success callback, the value of the "error" property - as an
342 error callback. </li>
343 </ul>
344
345
346 <br />&nbsp;<br /><h3><a class="none" name="dictionary">The HT-AJAX dictionary</a></h3>
347
348
349 <!-- Entry for MAKE-AJAX-PROCESSOR -->
350
351 <p><br />[Function]<br /><a class="none" name='make-ajax-processor'><b>make-ajax-processor</b> <i><tt>&amp;rest</tt> rest <tt>&amp;key</tt> type <tt>&amp;allow-other-keys</tt></i> =&gt; <i>new-ajax-processor</i></a></p>
352 <blockquote><br />
353
354 Creates an ajax-processor object. Parameters: <br />
355 TYPE - selects the kind of ajax-processor to use (should be
356 one of:SIMPLE or :LOKRIS, :PROTOTYPE, :YIU or :DOJO) (required). <br />
357 SERVER-URI - url that the ajax function calls will use (required). <br />
358 JS-FILE-URIS - a list of URLs on your server of the .js files that the
359 used library requires , such as lokris.js or prototype.js
360 (parameter required for all processors except :SIMPLE). If
361 only one file needs to be included then instead of a list a single
362 string may be passed. Also if this parameter is a string that ends
363 in a forward slash ( #\/ ) then it is assumed to be a directory
364 and the default file names for the processor are appended to it. <br />
365 AJAX-FUNCTION-PREFIX - the string to be prepended to the generated js functions,
366 (default prefix is &quot;ajax_&quot;). <br />
367 JS-DEBUG - enable the Javascript debugging function debug_alert(). Overrides
368 such parameters as JS-COMPRESSION and VIRTUAL-FILES. <br />
369 JS-COMPRESSION - enable Javascript compression of the generated code
370 to minimize the download size. <br />
371 VIRTUAL-JS-FILE - enable creation of virtual Javascript file instead of
372 inline Javascript code that may be
373 cached on the client to minimize traffic. <br />
374 </blockquote>
375
376 <!-- End of entry for MAKE-AJAX-PROCESSOR -->
377
378
379 <!-- Entry for EXPORT-FUNC -->
380
381 <p><br />[Generic function]<br /><a class="none" name='export-func'><b>export-func</b> <i>processor funcallable <tt>&amp;key</tt> method name content-type allow-cache</i> =&gt;| </a></p>
382 <blockquote><br />
383
384 Makes the function designated by FUNCALLABLE exported (available to call from js)
385 Parameters: <br />
386 METHOD - :get (default) or :post (:post is not supported under SIMPLE processor). <br />
387 NAME - export the function under a different name. <br />
388 CONTENT-TYPE - Value of Content-Type header so set on the reply
389 (default: text/plain). <br />
390 ALLOW-CACHE - (default nil) if true then HT-AJAX will not call NO-CACHE function and
391 allow to control cache manually. <br />
392 JSON - (default nil) if true, the function returns a JSON-encoded object that will
393 be decoded on the client and passed to the callback as an object<br />
394 <br />
395 Exporting the function (and later including the result of the GENERATE-PROLOGUE call in the
396 web page) makes available two functions for the Javascript code on the page:
397 <b>ajax_testfunc_callback</b> and <b>ajax_testfunc_set_element</b>. See
398 "<a href="#generated-js">Using the generated Javascript functions</a>"
399 for more details.
400 </blockquote>
401
402 <!-- End of entry for EXPORT-FUNC -->
403
404 <!-- Entry for UNEXPORT-FUNC -->
405
406 <p><br />[Generic function]<br /><a class="none" name='unexport-func'><b>unexport-func</b> <i>processor symbol-or-name</i> =&gt;| </a></p>
407 <blockquote><br />
408
409 Removes the previously exported function, should be called
410 with either the name (string) under which it was exported or the symbol
411 designating the function
412
413 </blockquote>
414
415 <!-- End of entry for UNEXPORT-FUNC -->
416
417
418 <!-- Entry for DEFUN-AJAX -->
419
420 <p><br />[Macro]<br /><a class="none" name='defun-ajax'><b>defun-ajax</b> <i>name params (processor <tt>&amp;rest</tt> export-args) declaration* statement*</i> </a></p>
421 <blockquote><br />
422
423 Macro, defining a function exported to AJAX
424 Example: (defun-ajax func1 (arg1 arg2) (*ajax-processor*)
425 (do-stuff))
426
427 </blockquote>
428
429 <!-- End of entry for DEFUN-AJAX -->
430
431
432 <!-- Entry for GENERATE-PROLOGUE -->
433
434 <p><br />[Generic function]<br /><a class="none" name='generate-prologue'><b>generate-prologue</b> <i>processor <tt>&amp;key</tt> use-cache</i> =&gt; <i>html-prologue</i></a></p>
435 <blockquote><br />
436
437 Generates the necessary HTML+JS to be included in the web page.
438 Provides caching if USE-CACHE is true (default).
439
440 </blockquote>
441
442 <!-- End of entry for GENERATE-PROLOGUE -->
443
444
445 <!-- Entry for GET-HANDLER -->
446
447 <p><br />[Generic function]<br /><a class="none" name='get-handler'><b>get-handler</b> <i>processor</i> =&gt; <i>handler</i></a></p>
448 <blockquote><br />
449
450 Get the hunchentoot handler for AJAX url.
451 The url that was passed as the SERVER-URI parameter (and all URLs starting with it)
452 should be dispatched to this handler.
453
454 </blockquote>
455
456 <!-- End of entry for GET-HANDLER -->
457
458
459 <br />&nbsp;<br /><h3><a class="none" name="supported-libraries"></a>Supported Javascript libraries</h3>
460 <ul>
461 <li>"Simple" - does not use an external library
462 </li>
463 <li><a href="http://www.ajax-buch.de/lokris/">Lokris</a> version 1.2 2006/08/02
464 </li>
465 <li><a href="http://prototypejs.org/">Prototype</a> version 1.5.0
466 </li>
467 <li><a href="http://dojotoolkit.org/">Dojo</a> version 0.4.1
468 </li>
469 <li><a href="http://developer.yahoo.com/yui/">Yahoo! User Interface (YUI) Library</a> versions 0.12.2 and 2.2.0
470 </li>
471 </ul>
472
473
474 <br />&nbsp;<br /><h3><a class="none" name="portability">Portability</a></h3>
475 <p> At the moment HT-AJAX is known to run on SBCL and Lispworks, but it aims to be
476 portable across all the implementations Hunchentoot runs on. Please report all incompatibilities.
477 </p>
478
479 <br />&nbsp;<br /><h3><a class="none" name="notes">Notes</a></h3>
480 <p><a class="note" name="note1">[1]</a>
481 When not using CREATE-PREFIX-DISPATCHER, note that not only the <b>SERVER-URI</b> itself
482 but also all the URLs starting with it need to be dispatched to the handler in order for
483 &quot;virtual .js files&quot; mechanism to function.
484 </p>
485
486 <p><a class="note" name="note2">[2]</a>
487 By default HTML-TEMPLATE escapes some characters while expanding. In the case of the prologue of
488 HT-AJAX there's no need to do it since HT-AJAX already wraps the generated Javascript code in the
489 proper CDATA sections (which also makes it possible to generate documents compliant with for
490 example XHTML Strict requirements). So one of the options is to wrap the template expansion
491 in the following binding:
492 </p>
493
494 <pre>
495 (let ((*string-modifier* #'CL:IDENTITY))
496 <i>...template expansion...</i> )
497 </pre>
498 <br />
499
500 <p><a class="note" name="note3">[3]</a>
501 The word &quot;unsafe&quot; means that it might not be generally safe to evaluate
502 arbitrary Javascript code coming from an untrusted source; in our case it's ok since
503 we control both the client and the server.
504 </p>
505
506 <br />&nbsp;<br /><h3><a class="none" name="ack">Acknowledgements</a></h3>
507
508 <p>
509 This documentation was prepared with the help of
510 <a href="http://weitz.de/documentation-template/">DOCUMENTATION-TEMPLATE</a>
511 by Edi Weitz (the code was hacked to run on SBCL).<br />
512 The initial inspiration for the SIMPLE processor came from Richard Newman's <a href="http://www.cliki.net/cl-ajax">CL-AJAX</a> which is designed for use with Araneida.
513 </p>
514
515 <br />
516 <a href="index.html">Back to the Lisp page</a>
517 <br />
518 <p style="font-size: x-small; text-color:#404040">
519 ;;; Copyright (c) 2007, Ury Marshak
520 <br />
521 </p>
522 </body>
523 </html>

  ViewVC Help
Powered by ViewVC 1.1.5