Commit 235d10eb82e0a042d672c5dbb70c688aca5e879a

Authored by Cédric RICARD
1 parent a91b94d1

Changing 'hidden()' interface

Showing 1 changed file with 4147 additions and 4147 deletions   Show diff stats
calexium_lib/web/CXM_making_a_web_site.anubis
1 -  
2 -  
3 - *Project* Anubis  
4 -  
5 - *Title* Making interactive Web sites.  
6 -  
7 - *Copyright* Copyright (c) Alain Prouté 2004-2005.  
8 - Copyright (c) Calexium 2007.  
9 -  
10 -  
11 - *Authors* Alain Prouté  
12 - David René  
13 -  
14 - *Revised* May 2007  
15 -  
16 -  
17 - *Overview*  
18 -  
19 - In this file we propose simple tools for making well structured interactive and secured  
20 - web sites.  
21 -  
22 -  
23 - ----------------------------------- Table of Contents ---------------------------------  
24 -  
25 - * (1) Structure of a web site.  
26 - ** (1.1) Three sorts of data.  
27 - ** (1.2) How requests are handled.  
28 - ** (1.3) What web pages are made of.  
29 - ** (1.4) Actions.  
30 - ** (1.5) States.  
31 -  
32 - * (2) Carrying on.  
33 - ** (2.1) Describing your web sites.  
34 - ** (2.2) Directories on the server's disk.  
35 - ** (2.3) Starting your web sites.  
36 -  
37 - * (3) The HTML interface.  
38 - ** (3.1) Types used by the HTML interface.  
39 - ** (3.2) ``in form'' versus ``off form''.  
40 - ** (3.3) Defining your own style.  
41 - ** (3.4) Actioners and forms.  
42 - ** (3.5) Local popup.  
43 -  
44 - ---------------------------------------------------------------------------------------  
45 -  
46 -  
47 -read tools/basis.anubis  
48 -read system/logger.anubis  
49 -read CXM_common.anubis  
50 -read CXM_multihost_http_server.anubis  
51 -read CXM_mime.anubis  
52 -  
53 -  
54 -  
55 - * (1) Structure of a web site.  
56 -  
57 - First of all we need to explain what a web site should be made of. Ideally, the  
58 - visitor (also called the 'client') should see the web site working as any other  
59 - interactive computer software. So, it should be clear that a 'session' (i.e. a visit  
60 - to the web site, including the consultation of several pages) is some kind of  
61 - conversation between the visitor and the web site, and that the web site should  
62 - maintain a 'current state' of this conversation. At each new request (click) from the  
63 - visitor, this state must be updated. This whole conversation is called a 'session' and  
64 - should not be confused with a single request.  
65 -  
66 -  
67 -  
68 - ** (1.1) Three sorts of data.  
69 -  
70 - All the data needed for putting a web site at work may be dispatched into three  
71 - categories:  
72 -  
73 - 1. Constant data (data that never change). These data may be hard coded into the  
74 - Anubis source files of the web site.  
75 -  
76 - 2. Permanent data (data which always exist independantly of the users connected to  
77 - the web site). These data are normally recorded into data bases.  
78 -  
79 - 3. Session data (data which depend on a particular visitor and which exist only  
80 - during the time he visits the web site). These data are stored into so-called  
81 - 'states'.  
82 -  
83 -  
84 - It is important to determine which data belongs to which category. This is part of your  
85 - design decisions.  
86 -  
87 -  
88 -  
89 - ** (1.2) How requests are handled.  
90 -  
91 - We want to separate the following two functionalities (which are used at each request  
92 - (click) during a single session):  
93 -  
94 - - computing the new state from the previous state and from the client request, and  
95 - updating the data base,  
96 -  
97 - - computing the page to be sent to the client from the new current state and from  
98 - the informations in the data base.  
99 -  
100 -  
101 - The next picture shows the structure we have in mind:  
102 -  
103 -  
104 - request +---------+ HTML page (with a hidden state name)  
105 - .-------------------| client |<--------------.  
106 - | .-----------------| | |  
107 - | | previous state +---------+ |  
108 - | | name (if any) |  
109 - | | | client side  
110 - ............................................................................  
111 - | | | server side  
112 - | | |  
113 - | | .-------------------. |  
114 - | | | previous state | |  
115 - V V V | |  
116 - +---------------+ +---------------+ +--------------+  
117 - | compute state | | server's disk | | compute page |  
118 - +---------------+ +---------------+ +--------------+  
119 - ^ | | ^ ^ ^ ^ ^  
120 - | | | | | | | |  
121 - | | `--------------------+--------------------' | |  
122 - | | new state | | |  
123 - read | `------------------------+--------------------' |  
124 - write | new state name |  
125 - update V |  
126 - +-----------+ |  
127 - | data base |--------------------------------------------'  
128 - +-----------+ read only  
129 -  
130 -  
131 - When the client begins a session, there is no previous state. In this case, a default  
132 - 'initial state' is used instead.  
133 -  
134 - The data base may be updated by 'compute state' box, but should not be update by the  
135 - 'compute page' box. The 'compute page' box should be allowed only to read the data  
136 - base.  
137 -  
138 - In this file, all the above stuff is defined, except the 'compute state' and 'compute  
139 - page' boxes. You just have to provide the function for computing a new state (compute  
140 - state) and the function for computing the page (compute page) from the new state. You  
141 - don't have to worry about state names, saving and retrieving states and the like.  
142 -  
143 -  
144 -  
145 -  
146 -  
147 - ** (1.3) What web pages are made of.  
148 -  
149 - What the client can see in his browser's window may be called a 'page'. Within a page,  
150 - we have several sorts of components:  
151 -  
152 - - 'local' components, i.e. all components which do not open a connection, like  
153 - texts, images, etc... possibly using JavaScript programmation,  
154 -  
155 - - 'actioners', which, when clicked upon, open a connection with our web site; they  
156 - may appear as links or buttons, etc...  
157 -  
158 - - 'foreign links', which when clicked upon, open a connection with another web site  
159 - (or ours eventually).  
160 -  
161 - Of course, what an actioner does is just ask our web site to perform an action. To that  
162 - end, the actioner essentially sends the name of the action to be performed. However, it  
163 - may be necessary to provide additional informations which may be seen as 'operands' of  
164 - the action. In order to attach operands to an action, HTML provides the notion of  
165 - 'form'. Indeed, a form contains essentially a set of input fields into which the client  
166 - may put values for the required operands of the action, and a submit button, which is  
167 - the actioner itself. Notice that a single form may contain several submit buttons,  
168 - which simply means that there are several distincts actions taking the same set of  
169 - operands.  
170 -  
171 - Restrictions must be put on the use of all theses gadgets. Indeed, for example,  
172 - putting a form within another form is officially meaningless in HTML, and the client's  
173 - browser may be seriously disturbed by this. In this file, we propose an interface to  
174 - the HTML language, which forbids such meaningless things, simply by imposing a strict  
175 - typing of HTML concepts.  
176 -  
177 - Each web site may be accessible through two communication channels:  
178 -  
179 - - a non secured channel (HTTP),  
180 - - a secured channel (HTTPS).  
181 -  
182 - Nevertheless, the whole thing should be considered as a single web site. For example,  
183 - you may have a secured page, obtained through HTTPS, containing public images obtained  
184 - through HTTP. An actioner in a non secured page may open a secured connection, and  
185 - conversely.  
186 -  
187 - Summarizing, a web page is made of local elements, foreign links and actioners.  
188 - Actioners receive operands from forms, and they also choose to communicate through the  
189 - non secured or through the secured channel.  
190 -  
191 -  
192 -  
193 - +-------------------+  
194 - | page |  
195 - | | +---------------+  
196 - | +--------------+ | | next page |  
197 - | | form | | | (non secured) |  
198 - | | +----------+ | | HTTP | |  
199 - | | | actioner |---------------------------->| |  
200 - | | +----------+ | | +---------------+  
201 - | | | |  
202 - | | +----------+ | | +---------------+  
203 - | | | actioner |---------------------------->| next page |  
204 - | | +----------+ | | HTTPS | (secured) |  
205 - | | | | | |  
206 - | +--------------+ | | |  
207 - | | +---------------+  
208 - | |  
209 - +-------------------+  
210 -  
211 -  
212 - Notice that actioners need no be necessarily put into forms. In that case, they work as  
213 - ordinary links, but they still may receive operands as we shall see.  
214 -  
215 -  
216 -  
217 -  
218 - ** (1.4) Actions.  
219 -  
220 - The client opens a new connection with our web site whenever he clicks on an  
221 - actioner. The result is that a request is sent, essentially made of a list of 'web  
222 - arguments'. Each web argument is a pair (name,value). One of these web arguments, the  
223 - 'action' web argument (whose name is "a"), determines the action to be performed. The  
224 - other web arguments (not including "s", used to identify the state) are the operands  
225 - for this action.  
226 -  
227 - Hence, the 'compute state' box in the picture above, splits naturally into as many  
228 - sub-boxes as there are actions. For this reason, we define the following type for  
229 - representing actions (where '$State' is the type representing session informations):  
230 -  
231 -public type Web_Action($SessionTicket, $State):  
232 - http_action (String name, // name of action  
233 - (Maybe($State)) -> Bool allow, // true if action allowed  
234 - (HTTP_Info http_info,  
235 - List(Web_arg) web_args, // actually only 'operands' web arguments  
236 - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),  
237 - https_action (String name, // name of action  
238 - (Maybe($State)) -> Bool allow, // true if action allowed  
239 - (HTTP_Info http_info,  
240 - List(Web_arg) web_args, // actually only 'operands' web arguments  
241 - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),  
242 - http_https_action (String name,  
243 - (Maybe($State)) -> Bool allow, // true if action allowed  
244 - (HTTP_Info http_info,  
245 - List(Web_arg) web_args,  
246 - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it).  
247 -  
248 - 'http_action's are executed only under HTTP, and 'https_action's are executed only  
249 - under HTTPS. 'http_https_action's may be executed under both types of connections.  
250 -  
251 - Each action has a name, which is used to identify the action. Each action also has a  
252 - function 'allow' whose job is to verify that the action is allowed in the current  
253 - state, and a function 'do_it' for performing the action. The function 'do_it' receives  
254 - a lot of informations:  
255 -  
256 - - 'HTTP informations':  
257 - - the IP address of the client,  
258 - - the URI requested by the client (after redirection),  
259 - - the list of HTTP headers generated by the client's browser,  
260 - - the list of web arguments sent by the client (except "s" and "a"),  
261 - - the previous state (or the 'initial' or 'ticket expired' state if no previous  
262 - state can be found).  
263 -  
264 - In most cases, HTTP informations are not used. This is the reason why they are gathered  
265 - for simplicity into a unique datum of type 'HTTP_Info'.  
266 -  
267 -// For your convenience, we introduce the following simpler variants:  
268 -//  
269 -//public define Web_Action($State)  
270 -// http_action  
271 -// (  
272 -// String name,  
273 -// $State -> Bool allow,  
274 -// (List(Web_arg),$State) -> $State do_it  
275 -// ) =  
276 -// http_action(name,  
277 -// (Maybe($State) ms) |-> if ms is  
278 -// {  
279 -// failure then true,  
280 -// success(s) then allow(s)  
281 -// },  
282 -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is  
283 -// {  
284 -// failure then (failure, []),  
285 -// success(s2) then (success(do_it(l,s2)), [])  
286 -// }).  
287 -//  
288 -//public define Web_Action($State)  
289 -// https_action  
290 -// (  
291 -// String name,  
292 -// $State -> Bool allow,  
293 -// (List(Web_arg),$State) -> $State do_it  
294 -// ) =  
295 -// https_action(name,  
296 -// (Maybe($State) ms) |-> if ms is  
297 -// {  
298 -// failure then true,  
299 -// success(s) then allow(s)  
300 -// },  
301 -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is  
302 -// {  
303 -// failure then (failure, []),  
304 -// success(s2) then (success(do_it(l,s2)), [])  
305 -// }).  
306 -//  
307 -//public define Web_Action($State)  
308 -// http_https_action  
309 -// (  
310 -// String name,  
311 -// $State -> Bool allow,  
312 -// (List(Web_arg),$State) -> $State do_it  
313 -// ) =  
314 -// http_https_action(name,  
315 -// (Maybe($State) ms) |-> if ms is  
316 -// {  
317 -// failure then true,  
318 -// success(s) then allow(s)  
319 -// },  
320 -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is  
321 -// {  
322 -// failure then (failure, []),  
323 -// success(s2) then (success(do_it(l,s2)), [])  
324 -// }).  
325 -  
326 -  
327 -  
328 - When you define your web site, you must provide the list of all the actions of the  
329 - site. When a new state has been computed, a graphical representation of this state  
330 - must be sent to the client. To that end, you must provide a function (named below  
331 - 'compute_page') of type:  
332 -  
333 - $State -> HTML_Page  
334 -  
335 - where the type 'HTML_Page' (defined below in this file) abstractly represents HTML  
336 - pages.  
337 -  
338 -public type HTML_Page:...  
339 -  
340 - It should be clear that states and pages are deeply linked together. Indeed, we really  
341 - understand the page shown to the client as a representation of the current state of the  
342 - conversation between the client and the web site, but also containing informations  
343 - taken from the data bases.  
344 -  
345 -  
346 -  
347 -  
348 -  
349 - ** (1.5) States.  
350 -  
351 - Now, we explain how you can define the type (say 'State') to be used as an instance of  
352 - the type parameter '$State'. The following is just a suggestion.  
353 -  
354 - Each state determines a page (since 'compute_page' computes a page from a  
355 - state). However, some components of the state may be independant of the page. It may be  
356 - the case for example for the indication of the natural language used by the  
357 - client. Hence, a state should be made of (at least) two parts:  
358 -  
359 - - informations which are the same for all pages,  
360 - - informations which are particular to each page.  
361 -  
362 - For example, you could define:  
363 -  
364 - type Page: // one alternative per page, with particular informations  
365 - login(...), // in the components  
366 - main_page(...),  
367 - ...etc...  
368 -  
369 - Now, the type 'State' could be defined as follows:  
370 -  
371 - type State:  
372 - state(Language, // informations valid for all pages  
373 - ...,  
374 - Page). // informations particular to a page  
375 -  
376 - However, if you are making a secured web site within which clients should be identified  
377 - (by id and password), it may be a good idea to have two sorts of states, one for non  
378 - identified clients and one for identified clients. In this case, define the type  
379 - 'State' as follows (this is just a suggestion):  
380 -  
381 - type State:  
382 - non_identified(Language),  
383 - identified(String id,  
384 - Language,  
385 - Page).  
386 -  
387 - When a request arrives, check if the previous state is 'identified(...)' or  
388 - 'non_identified(...)', and don't provide access to certain pages to non identified  
389 - clients. This is required for security.  
390 -  
391 - Some more words on security. If your site needs to identify clients, define the  
392 - initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and  
393 - password of the client. If the id and password are correct, then change the state of  
394 - the client to 'identified(...)'. No other action should be able to do that. Now, be  
395 - confident that clients cannot forge states. The only information they have is the name  
396 - of a state, not the state itself which is never sent over the network, but only stored  
397 - on the server's disk. The name of the state is constructed using strong cryptographical  
398 - methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even  
399 - state names cannot be seen by a third party. So, if the system retrieves a previous  
400 - state of the form 'identified(...)', you can be confident that your client is well  
401 - identified, and you can send him confidential informations.  
402 -  
403 - States have a limited life time. It may happen that a client clicks on a button at a  
404 - time its state is out of date. In this case, this system considers that the new state  
405 - is a special state named 'ticket expired'. You must provide a function producing this  
406 - state when you describe your web site. The page corresponding to this state must just  
407 - inform the client that he/she waited a too long time before clicking on a button, and  
408 - has to restart (a new conversation) from the begining.  
409 -  
410 -  
411 -  
412 - * (2) Carrying on.  
413 -  
414 - ** (2.1) Describing your web sites.  
415 -  
416 - Before you may start your web site, you must describe it, i.e. produce a datum of the  
417 - opaque type 'Web_Site'.  
418 -  
419 -public type Web_Site:...  
420 -  
421 - Producing such a datum may be performed by:  
422 -  
423 -public define Web_Site  
424 - make_web_site_description  
425 - (  
426 - List(String) common_names, // for example: ["www.our-business.com",  
427 - // "192.168.0.1"]  
428 - // the second one is just for testing  
429 - String site_directory, // where 'public' and other directories are  
430 - // located (should NOT end with '/')  
431 - One -> One init,  
432 - (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,  
433 - ($State expired,  
434 - HTTP_Info,  
435 - List(Web_arg),  
436 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,  
437 - (HTTP_Info,  
438 - List(Web_arg),  
439 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,  
440 - List(Web_Action($SessionTicket, $State)) actions,  
441 - (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,  
442 - Int32 timeout, // seconds (todo: minutes)  
443 - List(Redirection) redirections,  
444 - String charset,  
445 - List(String) journal_extensions,  
446 - List(String) journal_headers,  
447 - String authorization_secret,  
448 - List(MIME) known_mime_types,  
449 - (String action_name,  
450 - List(Web_arg) args)-> One before_send_file  
451 - ).  
452 -  
453 -  
454 - Explanations:  
455 -  
456 - 'common_names' is the list of names of the site (the name the browser must send as the  
457 - value of the 'Host' HTTP header in order to access the site must be in that list). Such  
458 - a name generally looks like this:  
459 -  
460 - www.somewhere.com  
461 -  
462 - If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of  
463 - the site must be exactly the same as the name on the certificate (which is precisely  
464 - called the 'common name' in the X.509 jargon). If the two names do not match, the site  
465 - will still work, but the transaction will not be transparent to the client. His browser  
466 - will complain that the name of the certificate does not match the name of the site, and  
467 - he will have to accept the certificate manually.  
468 -  
469 - 'site_directory' is the absolute path to the directory where the files needed by the  
470 - site are located. Usually this directory looks like:  
471 -  
472 - my_anubis/web_sites/www.somewhere.com  
473 -  
474 - However, this information is not computed from 'common_name', so that you can change  
475 - the common name (for example temporarily, for networking reasons) without loosing  
476 - access to the files.  
477 -  
478 - 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state  
479 - whose graphical representation is a page explaining to the user that its 'ticket' (or  
480 - 'session information') has expired, and that he/she must close all popup windows and  
481 - start a new session. The arguments of the function contain the previous (expired)  
482 - state and all current informations concerning the user. This arguments may be useful  
483 - for example for producing the expiration message in the language chosen by the user.  
484 - You can also (and this may be much smarter) send a 'ticket prolongation page'  
485 - (including a new login for example), and resume the same conversation, since you have  
486 - all the pertinent informations at hand. In the case the ticket is definitely lost, the  
487 - second fonction 'ticket_lost_state' is used.  
488 -  
489 - Notice that despite the fact that the parameter $State is involved in the arguments of  
490 - the above function, the type 'Web_Site' does not depend on this parameter. This allows  
491 - to produce lists of web site descriptions, where each description may be constructed  
492 - with a different instance of $State. This is required because distinct sites must have  
493 - distinct types of session informations. This is made possible by the fact that the  
494 - type is obscure, and the constructor replaced by a function which assembles  
495 - 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single  
496 - entity not depending on $State. You should have a look to the private part of this file  
497 - if you want more precisions about this programming technique.  
498 -  
499 - 'charset' is a string which will determine the character encoding to be used by the  
500 - browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",  
501 - etc...  
502 -  
503 - 'before_send_file' is a function which is executed just before the HTTP server sends a  
504 - file. It gets an action name and the web arguments received with the request for that  
505 - file. Notice that this action name and these web arguments may be put into a  
506 - 'private_download' element, and will come back to the server at the time of the  
507 - download.  
508 -  
509 -  
510 -  
511 - ** (2.2) Directories on the server's disk.  
512 -  
513 - The description of you site contains the name of the directory within which the  
514 - required files are located. This may be for example:  
515 -  
516 - my_anubis/web_sites/www.our-business.com/  
517 -  
518 - This is called the 'site directory' (for the given site). Within the site directory,  
519 - the following directories are created by this program:  
520 -  
521 - states  
522 - public  
523 - journal  
524 - private_download  
525 - upload_temporary  
526 -  
527 - The directory 'states' is used for storing states (session informations). Out of date  
528 - states are automatically removed after some time.  
529 -  
530 - The tree rooted at 'public' contains files that the server is allowed to send to the  
531 - clients. For security reasons, the server never sends a file which is not within the  
532 - tree whose root is this 'public' directory (except for the 'private download' mecanism;  
533 - see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')  
534 - must have been recognized before the file may be sent.  
535 -  
536 - The directory 'journal' contains the jounal files. The roles of the remaining  
537 - directories 'private_download' and 'upload_temporary' is explained in  
538 - 'multihost_http_server.anubis', where you will also find further informations on  
539 - 'public' and 'journal'.  
540 -  
541 -  
542 -  
543 -  
544 - ** (2.3) Web servers parameters.  
545 -  
546 - The web servers have several parameters useful for administration. They are described  
547 - as follows:  
548 -  
549 - public type WebServersParameters:  
550 - wsparms(Var(Bool) shutdown_required,  
551 -  
552 -  
553 -  
554 -  
555 - ** (2.4) Starting your web sites.  
556 -  
557 - When you have described all your web sites (you may want to have several web sites, and  
558 - they are distinguished by their 'common name'), you may start them all together using  
559 - 'start_web_sites' below. This function returns a result of the following type:  
560 -  
561 -public type Start_Web_Sites_Result:  
562 - cannot_bind_to_port(Int32),  
563 - cannot_bind_to_port(Int32,Int32),  
564 - ok(Server http_server,  
565 - Server https_server).  
566 -  
567 - Indeed, it may happen that the system cannot bind (begin to listen) to one of the two  
568 - ports (or to both). The main reason is that another server is already listening on that  
569 - port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that  
570 - the 's' bit has not been set for 'user' and 'group' (there is not such problem under  
571 - Windows). Also notice that the Linux kernel may need a rather long time (up to several  
572 - minutes) before liberating a listening port. Now, if the system can bind to the two  
573 - ports, the pair of the two servers is returned. Two tools are useful for manipulating  
574 - servers:  
575 -  
576 - shutdown of type Server -> One  
577 - is_down of type Server -> Bool  
578 -  
579 - They are defined in 'predefined.anubis' (together with the type 'Server').  
580 -  
581 -  
582 -public define Start_Web_Sites_Result  
583 - start_web_sites  
584 - (  
585 - Int32 ip_address, // the IP address shared by the web sites  
586 - Int32 http_port, // usually: 80  
587 - Int32 https_port, // usually: 443  
588 - String ssl_certificate_common_name,  
589 - List(Web_Site) web_sites, // web sites to be started  
590 - Var(Bool) shutdown_required  
591 - ).  
592 -  
593 - 'ip_address' is the IP address on which the two servers listen. If you put 0, the  
594 - servers listen on all the IP addresses of the machine. This may be useful if the  
595 - machine has several network interfaces.  
596 -  
597 - 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'  
598 - loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server  
599 - certificate. This is due to a problem of conception of SSL itself. See the book 'SSL  
600 - and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.  
601 -  
602 - Notice that the number of servers is always 2, regardless of the number of web sites  
603 - you are starting.  
604 -  
605 - The dynamic variable 'shutdown_required' may be used to control the shutdown of the two  
606 - servers from within the web site (typically the administration part). The servers will  
607 - shutdown as soon as this variable contains 'true'. So you must provide a variable  
608 - containing 'false' otherwise your servers will not run. You may also use the primitive  
609 - 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.  
610 -  
611 -  
612 -  
613 -  
614 -  
615 -  
616 - * (3) The HTML interface.  
617 -  
618 - We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination  
619 - of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more  
620 - reactive and attractive on the client side.  
621 -  
622 -  
623 - ** (3.1) Types used by the HTML interface.  
624 -  
625 - For easy reference, we gather below the definitions of all the types used by the HTML  
626 - interface, and we comment them immediately.  
627 -  
628 -  
629 -public type HTML_Size:  
630 - absolute(Int32), // in pixels  
631 - percentage(Int32).  
632 -  
633 -  
634 -public type Text_Option:  
635 - size(Int32), // size of character font to use  
636 - font(String), // name of character font to use (such as "helvetica",...)  
637 - color(RGB), // color to be used for characters  
638 - italic,  
639 - oblique,  
640 - small_capitals,  
641 - bold,  
642 - underlined,  
643 - left_justified,  
644 - right_justified,  
645 - justified, // justified on both sides  
646 - line_through,  
647 - nowrap,  
648 - class(String), //CSS class  
649 - id(String).  
650 -  
651 - A list of 'Text_Option' must be given with each text you want to put in your page.  
652 -  
653 - This indicate the way of reading text.  
654 -public type Reading_Way:  
655 - ltr, //the text is readable from "Left To Right" like english  
656 - rtl. //the text is readable from "Right To Left" like arabic  
657 -  
658 -  
659 -// Following types are define to prohib any arguments order error  
660 -  
661 -/**  
662 - * String value for 'id' attribut  
663 - */  
664 -public type HtmlId:  
665 - htmlId(String id).  
666 -  
667 -/**  
668 - * String value for 'class' attribut  
669 - */  
670 -public type HtmlClass:  
671 - htmlClass(String class).  
672 -  
673 -/**  
674 - * Name used in arguments list when form is submitted.  
675 - */  
676 -public type WebArgName:  
677 - wan(String name).  
678 -  
679 -/**  
680 - * Value passed to arguments list when form is submitted (for RadioButton and CheckBox).  
681 - */  
682 -public type WebArgValue:  
683 - wav(String value).  
684 -  
685 -/**  
686 - * Initial value of an input field.  
687 - */  
688 -public type InitialValue:  
689 - init(String value).  
690 -  
691 -public define Printable_tree [HtmlId x . Printable_tree y] = str_pt(x.id, y).  
692 -public define Printable_tree [HtmlClass x . Printable_tree y] = str_pt(x.class, y).  
693 -public define Printable_tree [WebArgName x . Printable_tree y] = str_pt(x.name, y).  
694 -public define Printable_tree [WebArgValue x . Printable_tree y] = str_pt(x.value, y).  
695 -public define Printable_tree [InitialValue x . Printable_tree y] = str_pt(x.value, y).  
696 -  
697 -  
698 -public type HtmlEvents:  
699 - // Form element events  
700 - onchange,  
701 - onsubmit,  
702 - onreset,  
703 - onselect,  
704 - onblur,  
705 - onfocus,  
706 - // Keyboard events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)  
707 - onkeydown,  
708 - onkeypress,  
709 - onkeyup,  
710 - // Mouse events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)  
711 - onclick,  
712 - ondblclick,  
713 - onmousedown,  
714 - onmousemove,  
715 - onmouseout,  
716 - onmouseover,  
717 - onmouseup.  
718 -  
719 -public type CoreAttrs:  
720 - id (String),  
721 - class (String),  
722 - style (String),  
723 - tooltip (String),  
724 - lang (String),  
725 - dir (Reading_Way),  
726 - accesskey (Word8),  
727 - tabindex (Int32),  
728 - attr (String, String),  
729 - event (HtmlEvents, String).  
730 -  
731 -public type InputAttrs:  
732 - class (String),  
733 - style (String),  
734 - tooltip (String),  
735 - lang (String),  
736 - dir (Reading_Way),  
737 - accesskey (Word8),  
738 - tabindex (Int32),  
739 - attr (String, String),  
740 - event (HtmlEvents, String).  
741 -  
742 -public type I18n:  
743 - lang (String),  
744 - dir (Reading_Way).  
745 -  
746 -//public type DIV_Option:  
747 -// id (String),  
748 -// class (String),  
749 -// style (String),  
750 -// tooltip (String),  
751 -// lang (String),  
752 -// dir (Reading_Way),  
753 -// attr (String, String),  
754 -// event (HtmlEvents, String).  
755 -//  
756 -  
757 - A list of 'DIV_Option' must be given with each DIV you want to put in your page.  
758 -  
759 -define String event_name  
760 - (  
761 - HtmlEvents e  
762 - ) =  
763 - if e is  
764 - {  
765 - // Form element events  
766 - onchange then "onchange",  
767 - onsubmit then "onsubmit",  
768 - onreset then "onreset",  
769 - onselect then "onselect",  
770 - onblur then "onblur",  
771 - onfocus then "onfocus",  
772 - // Keyboard events  
773 - onkeydown then "onkeydown",  
774 - onkeypress then "onkeypress",  
775 - onkeyup then "onkeyup",  
776 - // Mouse events  
777 - onclick then "onclick",  
778 - ondblclick then "ondblclick",  
779 - onmousedown then "onmousedown",  
780 - onmousemove then "onmousemove",  
781 - onmouseout then "onmouseout",  
782 - onmouseover then "onmouseover",  
783 - onmouseup then "onmouseup"  
784 - }.  
785 -  
786 -public type Table_Option:  
787 - background_color(RGB), // applied to all cells in the table  
788 - background_image(String url),  
789 - border(Int32 width_of_outer_edge, // if not present, all values are 0  
790 - Int32 width_of_top_of_relief,  
791 - Int32 width_of_inner_edge,  
792 - RGB border_color),  
793 - width(Int32), // sets a minimal width for the table  
794 - percentage_width(Int32).  
795 -  
796 -  
797 -public define Table_Option nude = border(0,0,0,rgb(0,0,0)).  
798 -  
799 -  
800 - A list of 'Table_Option' must be given with each table.  
801 -  
802 -  
803 -public type BackgroundOption:  
804 - repeat, // repeat the background in both directions  
805 - repeat_horizontal, // repeat the background only horizontally  
806 - repeat_vertical, // repeat the background only verticall  
807 - no_repeat, // don't repeat the background  
808 - center.  
809 -  
810 -  
811 -public type Cell_Option:  
812 - core_attrs(List(CoreAttrs)),  
813 - left, // put the content of the cell on the left  
814 - h_center, // center the content of the cell horizontally  
815 - right, // put the content of the cell on the right  
816 - top, // put the content of the cell upwards  
817 - v_center, // center the content of tye cell vertically,  
818 - bottom, // put the content of the cell downwards  
819 - base_line, // align the content vertically according to base lines  
820 - background_color(RGB),  
821 - background_image(String url, BackgroundOption),  
822 - width(Int32), // sets a minimal width for the cell  
823 - percentage_width(Int32),  
824 - height(Int32), // sets a minimal height for the cell  
825 - columns(Int32), // lets the cell span over several columns  
826 - rows(Int32), // lets the cell span over several rows  
827 - nowrap. // do not allow text wrapping within the cell  
828 -  
829 - A list of 'Cell_Option' must be given with each cell and each row in a table. Options  
830 - given with a row apply to all the cells in the row, but are superseded by options given  
831 - with cells, which apply only to the cell they are given with.  
832 -  
833 -  
834 -public type HTML_Cell($T):  
835 - cell(List(Cell_Option) options, $T content).  
836 -  
837 -public type HTML_Header_Cell($T):  
838 - header_cell(List(Cell_Option) options, $T content).  
839 -  
840 - The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',  
841 - depending on where you put your table (within a form or not within a form). For your  
842 - convenience, we define the following particular case:  
843 -  
844 -public define HTML_Cell($T)  
845 - cell  
846 - (  
847 - $T content  
848 - ) =  
849 - cell([],content).  
850 -  
851 -public define HTML_Header_Cell($T)  
852 - header_cell  
853 - (  
854 - $T content  
855 - ) =  
856 - header_cell([],content).  
857 -  
858 -  
859 -public type HTML_Row($T):  
860 - row(List(Cell_Option) options, List(HTML_Cell($T)) cells).  
861 -  
862 -public type HTML_Header_Row($T):  
863 - empty,  
864 - header_row(List(Cell_Option) options, List(HTML_Header_Cell($T)) cells).  
865 -  
866 - Same remark as for 'HTML_Cell($T)'. We define several convenience functions:  
867 -  
868 -public define HTML_Row($T)  
869 - row  
870 - (  
871 - List(HTML_Cell($T)) cells  
872 - ) =  
873 - row([],cells).  
874 -  
875 -public define HTML_Row($T)  
876 - row  
877 - (  
878 - HTML_Cell($T) cell  
879 - ) =  
880 - row([],[cell]).  
881 -  
882 -public define HTML_Header_Row($T)  
883 - header_row  
884 - (  
885 - List(HTML_Header_Cell($T)) cells  
886 - ) =  
887 - header_row([],cells).  
888 -  
889 -public define HTML_Header_Row($T)  
890 - header_row  
891 - (  
892 - HTML_Header_Cell($T) cell  
893 - ) =  
894 - header_row([],[cell]).  
895 -  
896 -public type Actioner_Connection:  
897 - same, // use same type of connection as current page  
898 - http, // use non secured connection  
899 - https. // use secured connection  
900 -  
901 -public type Other_Window_Option:  
902 - resizable, // the new window may be resized by the client  
903 - scrollbars, // the new window has scrollbars  
904 - width(Int32), // the new window has the specified width  
905 - height(Int32). // the new window has the specified height  
906 -  
907 -public type Actioner_Target:  
908 - same,  
909 - same (String label),  
910 - other(String window_name, List(Other_Window_Option)).  
911 -  
912 -public type Actioner_Aspect:  
913 - link (List(Text_Option),String text), // hypertext link  
914 - push_button (List(CoreAttrs),String text),  
915 - submit (List(CoreAttrs),String text),  
916 - button (String url_off, String url_on), // rollover button  
917 - button (String url_off, String url_on, Int32 w, Int32 h), // idem with size  
918 - immediate_selector (WebArgName name, Int32 size, List((WebArgValue,String)) choices),  
919 - immediate_selector (WebArgName name, Int32 size, List((WebArgValue,String)) choices, InitialValue selected).  
920 -  
921 -  
922 -  
923 -public type Actioner_Local_Action:  
924 - close_window.  
925 -  
926 -  
927 -public define Actioner_Aspect  
928 - link  
929 - (  
930 - String text  
931 - ) =  
932 - link([],text).  
933 -  
934 -  
935 -public define Actioner_Aspect  
936 - link  
937 - (  
938 - List(Text_Option) options,  
939 - Int32 i  
940 - ) =  
941 - link(options,integer_to_string(i)).  
942 -  
943 -public define Actioner_Aspect  
944 - link  
945 - (  
946 - Int32 i  
947 - ) =  
948 - link([],i).  
949 -  
950 -public define Actioner_Aspect  
951 - button  
952 - (  
953 - String url_img  
954 - ) =  
955 - button(url_img,url_img).  
956 -  
957 -  
958 -  
959 - Actioners are explained in details below.  
960 -  
961 -  
962 -public type TextAreaOption:  
963 - disabled,  
964 - read_only,  
965 - wrap_lines.  
966 -  
967 -public type HTML_In_Form:  
968 - literal_pt (Printable_tree),  
969 - literal (String),  
970 - sequence (List(HTML_In_Form) items),  
971 - text (List(Text_Option), String the_text),  
972 - preformated (List(Text_Option), String),  
973 - paragraph (List(Text_Option), HTML_In_Form content),  
974 - image (String url),  
975 - image (String url, Int32 width, Int32 height),  
976 - table (List(Table_Option), HTML_Header_Row(HTML_In_Form), List(HTML_Row(HTML_In_Form))),  
977 - center (HTML_In_Form),  
978 - mail_to (String email, HTML_In_Form element),  
979 - scroller (Int32 width, Int32 height,  
980 - Int32 content_width, Int32 content_height,  
981 - HTML_In_Form content),  
982 - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,  
983 - String action_name, List((String,String)) extra_ops,  
984 - List(Actioner_Local_Action)),  
985 - foreign_link (List(Text_Option), String url),  
986 - foreign_link (List(Text_Option), String url, String name),  
987 - private_download (String abs_path, String name, String extra_ext,  
988 - Maybe((String,List((String,String)))) action),  
989 - text_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),  
990 - text_input_ro (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),  
991 - password_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),  
992 - text_area (List(TextAreaOption), WebArgName name, InitialValue init, Int32 width, Int32 height),  
993 - file_upload (String label, HtmlId id, WebArgName name, Int32 width),  
994 - selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List(String) choices),  
995 - selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List(String) choices, InitialValue selected),  
996 - // List((String,String)) = List((code,name)) where :  
997 - // code is the web-arg value  
998 - // name appears in selector  
999 - selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List((WebArgValue,String)) choices),  
1000 - selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List((WebArgValue,String)) choices, InitialValue selected),  
1001 - radio_button (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),  
1002 - radio_button_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),  
1003 - check_box (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),  
1004 - check_box_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),  
1005 - div (List(CoreAttrs), HTML_In_Form content),  
1006 - div_empty (List(CoreAttrs)),  
1007 - hidden (String name, String value).  
1008 -  
1009 -  
1010 - 'HTML_In_Form' defines all the elements you may put within a form. We define a  
1011 - convenience function:  
1012 -  
1013 -public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).  
1014 -  
1015 -public define HTML_In_Form  
1016 - foreign_link  
1017 - (  
1018 - Int32 tsize,  
1019 - String url,  
1020 - String name  
1021 - ) =  
1022 - foreign_link([size(tsize)],url,name).  
1023 -  
1024 -public define HTML_In_Form  
1025 - actioner  
1026 - (  
1027 - Actioner_Connection conn,  
1028 - Actioner_Target targ,  
1029 - Actioner_Aspect asp,  
1030 - String action_name,  
1031 - List((String,String)) extra_ops  
1032 - ) =  
1033 - actioner(conn,targ,asp,action_name,extra_ops,[]).  
1034 -  
1035 -public define HTML_In_Form  
1036 - actioner  
1037 - (  
1038 - Actioner_Connection conn,  
1039 - Actioner_Target targ,  
1040 - Actioner_Aspect asp,  
1041 - ) =  
1042 - actioner(conn, targ, asp, "", [], []).  
1043 -  
1044 -  
1045 -public define HTML_In_Form  
1046 - text_area  
1047 - (  
1048 - WebArgName name,  
1049 - InitialValue init,  
1050 - Int32 width,  
1051 - Int32 height  
1052 - ) =  
1053 - text_area([],name,init,width,height).  
1054 -  
1055 -public define HTML_In_Form  
1056 - table  
1057 - (  
1058 - List(HTML_Row(HTML_In_Form)) rows  
1059 - ) =  
1060 - table([],empty,rows).  
1061 -  
1062 -public define HTML_In_Form  
1063 - table  
1064 - (  
1065 - List(Table_Option) options,  
1066 - List(HTML_Row(HTML_In_Form)) rows  
1067 - ) =  
1068 - table(options,empty,rows).  
1069 -  
1070 -public define HTML_In_Form  
1071 - table  
1072 - (  
1073 - HTML_Header_Row(HTML_In_Form) h_row,  
1074 - List(HTML_Row(HTML_In_Form)) rows  
1075 - ) =  
1076 - table([],h_row,rows).  
1077 -  
1078 -public define HTML_In_Form  
1079 - private_download  
1080 - (  
1081 - String abs_path,  
1082 - String name,  
1083 - String extra_ext  
1084 - ) =  
1085 - private_download(abs_path,name,extra_ext,failure).  
1086 -  
1087 -public define HTML_In_Form  
1088 - private_download  
1089 - (  
1090 - String abs_path,  
1091 - String name,  
1092 - String extra_ext,  
1093 - String action_name,  
1094 - List((String,String)) args  
1095 - ) =  
1096 - private_download(abs_path,name,extra_ext,success((action_name,args))).  
1097 -  
1098 -public define HTML_In_Form  
1099 - text  
1100 - (  
1101 - String s  
1102 - ) =  
1103 - text([],s).  
1104 -  
1105 -public define HTML_In_Form  
1106 - radio_button (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)  
1107 - = radio_button (options, label_text, htmlId(n.name), n, value, checked).  
1108 -  
1109 -public define HTML_In_Form  
1110 - radio_button_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)  
1111 - = radio_button_r (options, label_text, htmlId(n.name), n, value, checked).  
1112 -  
1113 -public define HTML_In_Form  
1114 - check_box (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)  
1115 - = check_box (options, label_text, htmlId(n.name), n, value, checked).  
1116 -  
1117 -public define HTML_In_Form  
1118 - check_box_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)  
1119 - = check_box_r (options, label_text, htmlId(n.name), n, value, checked).  
1120 -  
1121 -public define HTML_In_Form  
1122 - file_upload (WebArgName name, Int32 width)  
1123 - = file_upload("", htmlId(""), name, width).  
1124 -  
1125 -  
1126 -public type HTML_Off_Form:  
1127 - literal_pt (Printable_tree),  
1128 - literal (String),  
1129 - sequence (List(HTML_Off_Form) items),  
1130 - text (List(Text_Option), String the_text),  
1131 - preformated (List(Text_Option), String),  
1132 - paragraph (List(Text_Option), HTML_Off_Form content),  
1133 - image (String url),  
1134 - image (String url, Int32 width, Int32 height),  
1135 - table (List(Table_Option), HTML_Header_Row(HTML_Off_Form), List(HTML_Row(HTML_Off_Form))),  
1136 - center (HTML_Off_Form),  
1137 - mail_to (String email, HTML_Off_Form element),  
1138 - scroller (Int32 width, Int32 height,  
1139 - Int32 content_width, Int32 content_height,  
1140 - HTML_Off_Form content),  
1141 - fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),  
1142 - fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),  
1143 - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,  
1144 - String action_name, List((String,String)) extra_ops,  
1145 - List(Actioner_Local_Action)),  
1146 - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,  
1147 - String action_name, List((String,String)) extra_ops,  
1148 - List(Actioner_Local_Action), String form_name),  
1149 - foreign_link (List(Text_Option), String url),  
1150 - foreign_link (List(Text_Option), String url, String name),  
1151 - private_download (String abs_path, String name, String extra_ext,  
1152 - Maybe((String,List((String,String)))) action),  
1153 - label (String name),  
1154 - form (String form_name, List(CoreAttrs), HTML_In_Form content),  
1155 - form (String form_name, List(CoreAttrs),  
1156 - String action_name, List((String,String)) extra_ops,  
1157 - HTML_In_Form content),  
1158 - div (List(CoreAttrs), HTML_Off_Form content),  
1159 - div_empty (List(CoreAttrs)).  
1160 -  
1161 - 'HTML_Off_Form' defines all the elements you may put outside any form.  
1162 -  
1163 -  
1164 -public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).  
1165 -public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)  
1166 - = fixed_size_2(width,height,name_of_HTML_file).  
1167 -  
1168 -  
1169 -public define HTML_Off_Form  
1170 - foreign_link  
1171 - (  
1172 - Int32 tsize,  
1173 - String url,  
1174 - String name  
1175 - ) =  
1176 - foreign_link([size(tsize)],url,name).  
1177 -  
1178 -  
1179 -public define HTML_Off_Form  
1180 - actioner  
1181 - (  
1182 - Actioner_Connection conn,  
1183 - Actioner_Target targ,  
1184 - Actioner_Aspect asp,  
1185 - String action_name,  
1186 - List((String,String)) extra_ops  
1187 - ) =  
1188 - actioner(conn,targ,asp,action_name,extra_ops,[]).  
1189 -  
1190 -public define HTML_Off_Form  
1191 - table  
1192 - (  
1193 - List(HTML_Row(HTML_Off_Form)) rows  
1194 - ) =  
1195 - table([],empty,rows).  
1196 -  
1197 -public define HTML_Off_Form  
1198 - table  
1199 - (  
1200 - List(Table_Option) options,  
1201 - List(HTML_Row(HTML_Off_Form)) rows  
1202 - ) =  
1203 - table(options,empty,rows).  
1204 -  
1205 -public define HTML_Off_Form  
1206 - table  
1207 - (  
1208 - HTML_Header_Row(HTML_Off_Form) h_row,  
1209 - List(HTML_Row(HTML_Off_Form)) rows  
1210 - ) =  
1211 - table([],h_row,rows).  
1212 -  
1213 - We add two convenience functions for 'row'. The reason why we add two functions, one  
1214 - for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an  
1215 - arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,  
1216 - the arguments of the function do not refer to any of the types defined here.  
1217 -  
1218 -public define HTML_Row(HTML_In_Form)  
1219 - row  
1220 - (  
1221 - HTML_In_Form content  
1222 - ) =  
1223 - row([],[cell([],content)]).  
1224 -  
1225 -public define HTML_Row(HTML_Off_Form)  
1226 - row  
1227 - (  
1228 - HTML_Off_Form content  
1229 - ) =  
1230 - row([],[cell([],content)]).  
1231 -  
1232 -  
1233 -public define HTML_Off_Form  
1234 - private_download  
1235 - (  
1236 - String abs_path,  
1237 - String name,  
1238 - String extra_ext  
1239 - ) =  
1240 - private_download(abs_path,name,extra_ext,failure).  
1241 -  
1242 -  
1243 -public define HTML_Off_Form  
1244 - private_download  
1245 - (  
1246 - String abs_path,  
1247 - String name,  
1248 - String extra_ext,  
1249 - String action_name,  
1250 - List((String,String)) args  
1251 - ) =  
1252 - private_download(abs_path,name,extra_ext,success((action_name,args))).  
1253 -  
1254 -public define HTML_Off_Form  
1255 - text  
1256 - (  
1257 - List(Text_Option) lto,  
1258 - Int32 i  
1259 - ) =  
1260 - text(lto,integer_to_string(i)).  
1261 -  
1262 -  
1263 -public define HTML_Off_Form  
1264 - text  
1265 - (  
1266 - Int32 i  
1267 - ) =  
1268 - text([],i).  
1269 -  
1270 -public define HTML_Off_Form  
1271 - text  
1272 - (  
1273 - String s  
1274 - ) =  
1275 - text([],s).  
1276 -  
1277 - - Cell a gap between two other cells :  
1278 -  
1279 -public define HTML_Cell(HTML_Off_Form)  
1280 - width_gap  
1281 - (  
1282 - Int32 w  
1283 - ) =  
1284 - cell([width(w)],text([],"")).  
1285 -  
1286 -public define HTML_Cell(HTML_In_Form)  
1287 - width_gap  
1288 - (  
1289 - Int32 w  
1290 - ) =  
1291 - cell([width(w)],text([],"")).  
1292 -  
1293 -  
1294 - - Row a gap between two other rows :  
1295 -  
1296 -public define HTML_Row(HTML_Off_Form)  
1297 - height_gap  
1298 - (  
1299 - Int32 h  
1300 - ) =  
1301 - row([],[cell([height(h)],text([],""))]).  
1302 -  
1303 -public define HTML_Row(HTML_In_Form)  
1304 - height_gap  
1305 - (  
1306 - Int32 h  
1307 - ) =  
1308 - row([],[cell([height(h)],text([],""))]).  
1309 -  
1310 -  
1311 - Notice that the two types have alternatives in common (same name, same arguments types,  
1312 - up to the value of the parameter $T), which correspond to elements which may be put  
1313 - anywhere in the page.  
1314 -  
1315 -  
1316 -  
1317 -public type CSS_Style:  
1318 - text_options(List(Text_Option)).  
1319 -  
1320 -public type CSS_File:  
1321 - css_file(String file_name).  
1322 -  
1323 -public type JS_File:  
1324 - js_file(String file_name).  
1325 -  
1326 -define String  
1327 - format  
1328 - (  
1329 - List(Text_Option) l  
1330 - ).  
1331 -  
1332 -  
1333 -  
1334 -  
1335 -define Printable_tree  
1336 - format_css_styles  
1337 - (  
1338 - List(CSS_Style) l  
1339 - ) =  
1340 - if l is  
1341 - {  
1342 - [ ] then [ ],  
1343 - [h . t] then  
1344 - [ if h is  
1345 - {  
1346 - text_options(tos) then  
1347 - [" body, span, p { ", format(tos), " }\n" ]  
1348 - }  
1349 - . format_css_styles(t)]  
1350 - }.  
1351 -  
1352 -  
1353 -  
1354 -public type HTML_Meta:  
1355 - keywords (List(String)),  
1356 - refresh (Actioner_Connection connection,  
1357 - Actioner_Target target,  
1358 - String action_name,  
1359 - Int32 delay), // in seconds  
1360 - meta (String name, String content),  
1361 - http_equiv (String name, String content),  
1362 - generic_meta (List((String,String))),  
1363 - literal (String).  
1364 -  
1365 - Meta tags are put in the 'head' of the HTML page.  
1366 -  
1367 -  
1368 -public type Body_Option:  
1369 - background_color (RGB),  
1370 - background_image (String url),  
1371 - background_image (String url, List(BackgroundOption)).  
1372 -  
1373 -public type HTML_Body:  
1374 - body(List(Body_Option) options, HTML_Off_Form content).  
1375 -  
1376 -  
1377 -public type HTML_Page:  
1378 - html_page(String title,  
1379 - List(HTML_Meta) meta_tags,  
1380 - List(CSS_Style) styles,  
1381 - List(CSS_File) css_files,  
1382 - List(JS_File) js_files,  
1383 - HTML_Body body).  
1384 -  
1385 -public define HTML_Page  
1386 - html_page  
1387 - (  
1388 - String title,  
1389 - List(HTML_Meta) metas,  
1390 - HTML_Body body  
1391 - ) =  
1392 - html_page(title,metas,[],[],[],body).  
1393 -  
1394 -public define HTML_Page  
1395 - html_page  
1396 - (  
1397 - String title,  
1398 - List(HTML_Meta) metas,  
1399 - List(CSS_Style) styles,  
1400 - HTML_Body body  
1401 - ) =  
1402 - html_page(title, metas, styles, [], [], body).  
1403 -  
1404 - 'HTML_Page' represents the final product of the construction of a web page.  
1405 -  
1406 -  
1407 -  
1408 -  
1409 - *** (3.2) ``in form'' versus ``off form''.  
1410 -  
1411 - There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some  
1412 - of them may have a content, which is yet another HTML element (or several). Hence, it  
1413 - is meaningful to say that an element is 'within' another one. Now, putting any element  
1414 - within any other one may be meaningless. For example, an input element must be put  
1415 - within a form (otherwise, it is useless), and a from within another form has no precise  
1416 - meaning (and is forbidden by the HTML specification).  
1417 -  
1418 - Actually, the main criterium is ``within a form or not within a form''. So, HTML  
1419 - elements in a given page are separated into two categories: those who are within a  
1420 - form, and the others. Nevertheless, there are elements which may belong to both  
1421 - categories, like images and texts. We want to make use of the strong typing mecanism of  
1422 - Anubis in order to forbid non meaningful placement of elements.  
1423 -  
1424 - The type 'HTML_In_Form' defines elements to be put within forms. Similarly,  
1425 - 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,  
1426 - and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),  
1427 - but the two types are not cross recursive. This is the reason why it is impossible to  
1428 - put a form within a form. In order to construct a web page, you essentially have to  
1429 - produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').  
1430 -  
1431 - In practice, you don't have to worry so much about these two types, because elements  
1432 - which may be put anywhere are constructed for both types by functions with the same  
1433 - name and the same arguments. Hence, for both types, you just write the same thing. You  
1434 - are warned by the compiler only when you try to put an element at a place it is not  
1435 - allowed.  
1436 -  
1437 -  
1438 -  
1439 - *** (3.3) Defining your own style.  
1440 -  
1441 - We provide generic tools for constructing HTML elements. However, your web site needs  
1442 - to have a ``style''.  
1443 -  
1444 - To that end, you need to write down a set of ``styling functions'', using the tools  
1445 - defined here. These styling functions allow the introduction of your colors and other  
1446 - visual characteristics into the constructed elements once and for all. For example, you  
1447 - may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using  
1448 - some 'text_color'. You may write something like this:  
1449 -  
1450 - define RGB text_color = rgb(10,40,40).  
1451 -  
1452 - define HTML_Off_Form  
1453 - text  
1454 - (  
1455 - String the_text  
1456 - ) =  
1457 - text([font("helvetica"),size(14),color(text_color)],  
1458 - the_text).  
1459 -  
1460 - (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a  
1461 - page, you just write:  
1462 -  
1463 - text("... some text ...")  
1464 -  
1465 - and you don't have to provide the font, size and color for each text. If you want to  
1466 - have several styles of text presentation, you just write several sets of such  
1467 - convenience functions. This also suggests a trick. You may want for example different  
1468 - colors for 'in form' texts and 'off form' texts. This may be achieved automatically by  
1469 - defining two functions as above, with the same name and same argument type, but  
1470 - returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.  
1471 -  
1472 - If this preliminary work is well done, you will not waste your time later when you  
1473 - concentrate on the actual informational content of your pages.  
1474 -  
1475 - This general principle should be applied to all sorts of elements. This is the best  
1476 - thing to do in order to separate the functions defining the visual style from the  
1477 - functions defining the informational content itself, so that changing the style without  
1478 - changing the content becomes easy. This is also the best way for having a clean and  
1479 - easily readable source for your web site.  
1480 -  
1481 -  
1482 -  
1483 -  
1484 -  
1485 - *** (3.4) Actioners and forms.  
1486 -  
1487 - We have gathered several notions from HTML into that of an 'actioner'. An actioner is  
1488 - an HTML element which opens a connection to our server when clicked upon. Actioners may  
1489 - have different visual aspects. They may look like hypertext links or like buttons  
1490 - (rollovers), or even like selectors (with immediate action). In any case, their  
1491 - behavior is the same: they open a connection to our server, and send a set of 'web  
1492 - arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes  
1493 - the action to be performed, and the others should be considered as operands for this  
1494 - action. Actually, the precise behavior of the actioner has several variants.  
1495 -  
1496 - The connection with the server may be secured (HTTPS) or non secured (HTTP). See the  
1497 - type 'Actioner_Connection' above.  
1498 -  
1499 - You must also choose where the answer must be rendered. This may be in the same window  
1500 - or in another window (or frame). If it is in another window, the name of that window  
1501 - must be given. If the window does not exist, the browser will create it. Optionally,  
1502 - you may give the dimensions of the new window and other characteristics. See the type  
1503 - 'Actioner_Target' above.  
1504 -  
1505 - The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the  
1506 - case of a rollover button, you provide the URLs of two images (of the same size)  
1507 - representing the button:  
1508 -  
1509 - url_off: to be used when the mouse is not over the button,  
1510 - url_on: to be used when the mouse is over the button.  
1511 -  
1512 - You can also create rollover buttons without creating images. Just use the second  
1513 - alternative named 'button'. The server creates the images automatically.  
1514 -  
1515 - The purpose of forms is just to give operands to actioners. If the actioner is placed  
1516 - within a form, all the input elements which are within this form provide operands to  
1517 - the actioner (except sometimes when they are not set by the client). If it is not put  
1518 - within a form, the actioner gets no operand, except if the name of a form is explicitly  
1519 - given, in which case the actioner gets all the inputs from that form as  
1520 - operands. Furthermore, you may want to give extra operands to the actioner. This may be  
1521 - useful for separating families of actioners with the same action name. Extra operands  
1522 - 'name=value' must be given in the form of pairs '(name,value)'.  
1523 -  
1524 - Notice that the name of a form may be used by an actioner which is off the form, so as  
1525 - to get the operands provided by this form. Also notice that several actioners may refer  
1526 - to the same form, being either in the form, or referring to the form from the  
1527 - outside. These actioners simply get the same set of operands, even if they correspond  
1528 - to distinct actions.  
1529 -  
1530 - Input elements may be put only within a form.  
1531 -  
1532 -  
1533 -  
1534 - *** (3.5) Local popup.  
1535 -  
1536 - This element looks like a link or a rollover button. When this button is clicked upon,  
1537 - a 'popup window' appears. Actually, this popup window is just a layer in the same HTML  
1538 - page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In  
1539 - particular, clicking on the button does not open any connection. This is why it is  
1540 - called 'local'. The arguments have the following roles:  
1541 -  
1542 - Actioner_Aspect aspect of the button (same semantics as for actioners)  
1543 - content content of the popup window  
1544 - x, y, position of the popup window on the HTML page (not relative  
1545 - to the button but to the page itself)  
1546 - title title of the popup window  
1547 - color color of the title bar and close button in the popup window.  
1548 - A lightened version of this color is used for the background  
1549 - of the popup window.  
1550 - width width of the title bar  
1551 -  
1552 -  
1553 -  
1554 -  
1555 -  
1556 - --- That's all for the public part ! --------------------------------------------------  
1557 -  
1558 -  
1559 -  
1560 -  
1561 -  
1562 - ----------------------------------- Table of Contents ---------------------------------  
1563 -  
1564 - *** [1] States.  
1565 - *** [1.1] Saving and retrieving states.  
1566 - *** [1.2] Deleting out of date states.  
1567 -  
1568 - *** [2] Tools.  
1569 - *** [2.1] Directories.  
1570 - *** [2.2] Secondary documents.  
1571 -  
1572 - *** [3] Managing web arguments.  
1573 - *** [3.1] Prefixing web arguments names.  
1574 - *** [3.2] Separating web arguments.  
1575 - *** [3.3] Applying an action.  
1576 -  
1577 - *** [4] Web site descriptions and the 'awp handlers'.  
1578 - *** [4.1] The type 'Web_Site'.  
1579 - *** [4.2] Making a web site description.  
1580 - *** [4.3] Starting the servers.  
1581 -  
1582 - *** [5] HTML Formating.  
1583 - *** [5.1] The type 'HTML_Any($T)'.  
1584 - *** [5.2] Formating a color.  
1585 - *** [5.3] Creating buttons.  
1586 - *** [5.4] Formating an actioner.  
1587 - *** [5.5] Formating a private download link.  
1588 - *** [5.6] Formating rows and cells in a table.  
1589 - *** [5.7] Formating elements which may be put anywhere.  
1590 - *** [5.8] Formating 'in form' elements.  
1591 - *** [5.9] Formating 'off form' elements.  
1592 - *** [5.10] Formating meta-tags.  
1593 -  
1594 - ---------------------------------------------------------------------------------------  
1595 -  
1596 -  
1597 -  
1598 -  
1599 -public define String  
1600 - doctype_w3c_header  
1601 - =  
1602 - "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "+  
1603 - "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".  
1604 -  
1605 -  
1606 -  
1607 -  
1608 - *** [1] States.  
1609 -  
1610 - We have to define functions for saving a state, retrieving a state, deleting out of  
1611 - date states. We need one such function per web site. The types of the first two  
1612 - functions depend on the parameter $State. This is not the case of the third one. The  
1613 - fact that the instance of $State is variable from one web sites to the other implies  
1614 - rather subtle manipulations using full functionality.  
1615 -  
1616 -  
1617 - *** [1.1] Saving and retrieving states.  
1618 -  
1619 - Each state is saved into a file on the server's disk (in the directory represented by  
1620 - the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The  
1621 - state is saved together with a time stamp whose value is obtained by adding the current  
1622 - time to the given timeout for states. The state receives a name obtained by hashing  
1623 - (using sha1) the content of the file itself, and then encoding the hash with  
1624 - 'web_arg_encode'. The name of the file into which the state is saved is the  
1625 - concatenation of "s" and the name of the state.  
1626 -  
1627 -read CXM_web_arg_encode.anubis  
1628 -  
1629 - The tool below constructs the function which is able to save a state on the server's  
1630 - disk.  
1631 -  
1632 -define (Maybe($State) s) -> String // the function constructed returns the name of the state  
1633 - make_save_state_function  
1634 - (  
1635 - Int32 timeout,  
1636 - String state_directory  
1637 - ) =  
1638 - (Maybe($State) mbs) |->  
1639 - if mbs is  
1640 - {  
1641 - failure then "",  
1642 - success(s) then  
1643 - with time_stamp = now+timeout,  
1644 - to_be_saved = (time_stamp,s),  
1645 - state_name = web_arg_encode(sha1(s)),  
1646 - if save(to_be_saved,state_directory+"/s"+state_name) is ok  
1647 - then state_name  
1648 - else (print("Cannot create state file in '"+state_directory+"'.\n"); "")  
1649 - }.  
1650 -  
1651 -  
1652 - When a request arrives, we need to retrieve the previous state from the server's  
1653 - disk. We receive the name of that state. If the state is out of date, the state file is  
1654 - kept 3 days, and then deleted.  
1655 -  
1656 -type PreviousState($State):  
1657 - not_found, // cannot retrieve the previous state  
1658 - out_of_date($State), // the previous state is out of date  
1659 - still_valid($State). // the previous state is still valid  
1660 -  
1661 -define (String state_name) -> PreviousState($State)  
1662 - make_retrieve_state_function  
1663 - (  
1664 - String state_directory  
1665 - ) =  
1666 - (String state_name) |->  
1667 - with file_path = state_directory+"/s"+state_name,  
1668 - if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)  
1669 - then (  
1670 - if d is (time_stamp,s) then  
1671 - if time_stamp < now  
1672 - then (  
1673 - forget(remove(file_path));  
1674 - out_of_date(s)  
1675 - )  
1676 - else still_valid(s) // state has been successfully retrieved  
1677 - )  
1678 - else not_found.  
1679 -  
1680 -  
1681 -  
1682 - *** [1.2] Deleting out of date states.  
1683 -  
1684 - We also need to delete states which are out of date and which will never be deleted by  
1685 - the above method. This may be performed by a machine doing this periodically (say once  
1686 - per states life time period).  
1687 -  
1688 -define (List(String) file_names) -> One  
1689 - make_delete_out_of_date_states_function  
1690 - (  
1691 - Maybe($State) dummy,  
1692 - String state_directory  
1693 - ) =  
1694 - (List(String) file_names) |-df->  
1695 - if file_names is  
1696 - {  
1697 - [ ] then unique,  
1698 - [h . t] then  
1699 - with file_path = state_directory+"/"+h,  
1700 - if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)  
1701 - then (  
1702 - if d is (time_stamp,data) then  
1703 - if time_stamp < now  
1704 - then (forget(remove(file_path)); df(t))  
1705 - else df(t)  
1706 - )  
1707 - else (forget(remove(file_path)); df(t))  
1708 - }.  
1709 -  
1710 -  
1711 - The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.  
1712 -  
1713 - Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the  
1714 - function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler  
1715 - does not accept a parameter in the body of a function (here the parameter is required  
1716 - by the use of 'retrieve') if this parameter does not appear in the type of the  
1717 - function. This is because this would create ambiguities that no explicit typing may  
1718 - ever resolve. If you put a double slash in front of the declaration of 'dummy' above,  
1719 - and if you compile this file, you will get a message like this one:  
1720 -  
1721 - Error in 'making_a_web_site.anubis', line 1300, column 7:  
1722 - A definition may not contain parameters which are not present  
1723 - in the declaration part (hidden parameters):  
1724 - $State  
1725 -  
1726 - The type of the function constructed by 'make_delete_out_of_date_states_function' is  
1727 - independant of the parameter $State. This is important because this allows to create  
1728 - the list of such functions for all web sites. From this list, it is possible to call  
1729 - the functions one after the other, so deleting out of date states for all web  
1730 - sites. Actually, the next function receives a list of pairs (state_directory,function),  
1731 - one for each web site.  
1732 -  
1733 -define One  
1734 - delete_out_of_date_states // for all web sites  
1735 - (  
1736 - List((String, List(String) -> One)) directories_and_functions  
1737 - ) =  
1738 - if directories_and_functions is  
1739 - {  
1740 - [ ] then unique,  
1741 - [h . t] then if h is (state_directory,function) then  
1742 - function(directory_list(state_directory,"s*"));  
1743 - delete_out_of_date_states(t)  
1744 - }.  
1745 -  
1746 -  
1747 - The above function must be called periodically in a separate virtual machine. The  
1748 - period we have choosen is (rather logically) the life time of states itself. This may  
1749 - be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not  
1750 - be really infinite, because the servers may be shutdown. Hence, our loop must test  
1751 - (rather frequently; say every second) if the servers are down. If they are, the loop  
1752 - must be exited.  
1753 -  
1754 -define One  
1755 - delete_states_loop  
1756 - (  
1757 - List((String,List(String) -> One)) directories_and_functions,  
1758 - Int32 timeout,  
1759 - Int32 next_time,  
1760 - Server http_server,  
1761 - Server https_server,  
1762 - Var(Bool) shutdown_required  
1763 - ) =  
1764 - if *shutdown_required  
1765 - then (shutdown(http_server); shutdown(https_server))  
1766 - else unique;  
1767 - if (is_down(http_server) & is_down(https_server))  
1768 - then unique  
1769 - else if now > next_time  
1770 - then  
1771 - (  
1772 - delete_out_of_date_states(directories_and_functions);  
1773 - delete_states_loop(directories_and_functions,  
1774 - timeout,  
1775 - now+timeout,  
1776 - http_server,  
1777 - https_server,  
1778 - shutdown_required)  
1779 - )  
1780 - else  
1781 - (  
1782 - sleep(1000); // sleep just one second and try again  
1783 - delete_states_loop(directories_and_functions,  
1784 - timeout,  
1785 - next_time,  
1786 - http_server,  
1787 - https_server,  
1788 - shutdown_required)  
1789 - ).  
1790 -  
1791 - The above loop must be run in a separate virtual machine. This will be done just after  
1792 - the two servers are started.  
1793 -  
1794 -  
1795 -  
1796 -  
1797 -  
1798 - *** [2] Tools.  
1799 -  
1800 - *** [2.1] Directories.  
1801 -  
1802 - We need a tool for creating directories (if needed).  
1803 -  
1804 - (This tool has been moved to 'tools/basis.anubis').  
1805 -  
1806 -  
1807 -  
1808 -  
1809 - *** [2.2] Secondary documents.  
1810 -  
1811 - Some HTML elements (like '<object>', '<frame>') cannot receive their content directly  
1812 - from the current document, but only through an URL. For this reason, we implement a  
1813 - mecanism for creating secondary documents on the fly. To that end we use the 'private  
1814 - download' mecanism.  
1815 -  
1816 - A secondary document is formated by the same functions as the main document itself. The  
1817 - next function takes an 'off form' element, creates the file containing the secondary  
1818 - document in HTML format, and returns the URL at which the document will be available.  
1819 -  
1820 -define String  
1821 - create_secondary_document  
1822 - (  
1823 - String sd, // site directory  
1824 - String as, // authorization_secret  
1825 - String sn, // state name  
1826 - $T -> Printable_tree format_element,  
1827 - $T content,  
1828 - HTML_Size width  
1829 - ) =  
1830 - with private_download_directory = sd+"/private_download",  
1831 - hash = web_arg_encode(sha1(content)),  
1832 - file_content = (Printable_tree)  
1833 - [doctype_w3c_header,  
1834 - "<html><body><table ",  
1835 - if width is  
1836 - {  
1837 - absolute(w) then ["width=\"",w-25],  
1838 - percentage(w) then ["width=\"95%\""]  
1839 - },"\"><tbody><tr><td align=right>",  
1840 - format_element(content),  
1841 - "</td></tr></tbody></table></body></html>"  
1842 - ],  
1843 - file_name = "sd"+hash+".html",  
1844 - file_path = private_download_directory+"/"+file_name,  
1845 - if write_to_file(file_path,file_content) is  
1846 - {  
1847 - cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",  
1848 - write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",  
1849 - ok then file_name+"?zauth="+  
1850 - make_authorization(sd,as,private_download_directory+"/"+file_name)  
1851 - }.  
1852 -  
1853 -  
1854 -  
1855 -  
1856 - *** [2.3] Generating unique ids.  
1857 -  
1858 - In order to uniquely name object for JavaScript we generate unique ids from a counter.  
1859 -  
1860 -define Int32  
1861 - new_idnum  
1862 - (  
1863 - Var(Int32) ic_v // 'idnum' counter variable  
1864 - ) =  
1865 - protect  
1866 - with result = *ic_v+1,  
1867 - ic_v <- result;  
1868 - result.  
1869 -  
1870 -  
1871 -  
1872 -  
1873 -  
1874 -  
1875 - *** [3] Managing web arguments.  
1876 -  
1877 - Web arguments are those pairs 'name=value' which are transmitted through the HTTP  
1878 - protocol. We need precise naming conventions for these web arguments.  
1879 -  
1880 -  
1881 -  
1882 - *** [3.1] Prefixing web arguments names.  
1883 -  
1884 - We want to assign different roles to web arguments, and we also want to be able to  
1885 - recognize its role directly from the name of a web argument. The name "s" is reserved  
1886 - for the web argument whose value is the name of the current state. The name "a" is  
1887 - reserved for the web argument whose value is the name of the action to be  
1888 - performed. Other web arguments receive arbitrary names, and in order to avoid clashes,  
1889 - these names are prefixed by:  
1890 -  
1891 - "p" for names of password inputs,  
1892 - "o" for other web arguments  
1893 -  
1894 - The reason why password input names have a distinct prefix is that this allows the HTTP  
1895 - server to hide the passwords on the console of the server and in the journal.  
1896 -  
1897 -  
1898 -  
1899 -  
1900 - *** [3.2] Separating web arguments.  
1901 -  
1902 - When a new request arrives, we need to separate the web arguments, that is to say:  
1903 -  
1904 - - find the value of "s", and recover the corresponding state,  
1905 - - find the value of "a", which is the name of the action to be performed,  
1906 - - get the list of all the remaining web arguments (operands of the action).  
1907 -  
1908 - We must also determine if the previous state may be recovered. If it is not the case  
1909 - (either because the previous state name is invalid, or the previous state is out of  
1910 - date), we must check if there is an action name. Indeed, the presence of an action name  
1911 - indicates that the user has clicked on one of our buttons or links. If on the contrary  
1912 - there is no action name the user has just entered our address in his browser. In this  
1913 - last case, we must send the first page of our site (maybe a 'login' page), but if there  
1914 - is an action, we must send a page just saying that the session ticket has expired. If  
1915 - the previous state is recovered and there is no action, the new state is the same as  
1916 - the previous state.  
1917 -  
1918 - The result of the separation of the web arguments is of type:  
1919 -  
1920 -type Separated_Web_Args($State):  
1921 - swa(Maybe(PreviousState($State)) previous_state,  
1922 - Maybe(String) action_name,  
1923 - List(Web_arg) operands).  
1924 -  
1925 -  
1926 -  
1927 - The next function constructs the function which separates the web arguments.  
1928 -  
1929 -define (List(Web_arg) lwa) -> Separated_Web_Args($State)  
1930 - make_separate_web_args_function  
1931 - (  
1932 - String state_directory,  
1933 - String -> PreviousState($State) retrieve_state  
1934 - ) =  
1935 - (List(Web_arg) lwa) |-swaf->  
1936 - if lwa is  
1937 - {  
1938 - [ ] then  
1939 - //  
1940 - // no web arg found => no previous state and no action  
1941 - //  
1942 - swa(failure,failure,[]),  
1943 -  
1944 - [wa_1 . wa_others] then  
1945 - //  
1946 - // at least one web arg =>  
1947 - // separate other web args, and insert the first one as needed  
1948 - //  
1949 - if (Separated_Web_Args($State))swaf(wa_others) is  
1950 - {  
1951 - swa(ps1, // possible previous state  
1952 - an1, // maybe an action name  
1953 - op1) // operands so far  
1954 - then  
1955 - if wa_1 is  
1956 - {  
1957 - web_arg(n,v) then  
1958 - with prefix = if substr(n,0,4) = "amp;" then substr(n,4,1) else substr(n,0,1),  
1959 - name_start = (Int32)(if substr(n,0,4) = "amp;" then 5 else 1),  
1960 - if prefix = "s" then  
1961 - swa(success(retrieve_state(v)),an1,op1) else  
1962 - if prefix = "a" then  
1963 - swa(ps1,success(v),op1) else  
1964 - if prefix = "t" then  
1965 - swa(ps1,an1,[web_arg("target",v) . op1]) else  
1966 - if prefix = "p" then  
1967 - swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else  
1968 - if prefix = "o" then  
1969 - swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else  
1970 - swa(ps1,an1,op1),  
1971 -  
1972 - upload(n,v,t) then  
1973 - swa(ps1,an1,[upload(substr(n,1,length(n)-1),v,t) . op1])  
1974 - }}  
1975 - }.  
1976 -  
1977 -  
1978 -  
1979 -  
1980 -  
1981 -  
1982 - *** [3.3] Applying an action.  
1983 -  
1984 - When the web arguments are separated (and their names cleaned up from prefixes), we may  
1985 - apply the action to the operands and the current state. We search for the action to be  
1986 - applied in the list of actions. If no action is found, the new state is the same as  
1987 - the previous state. Also, we deny the application of an HTTP action if the request  
1988 - arrives through the HTTPS channel and conversely.  
1989 -  
1990 -  
1991 -define (Maybe($State) previous,  
1992 - String action_name,  
1993 - HTTP_Info http_info,  
1994 - List(Web_arg) lwa,  
1995 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header))  
1996 - make_apply_action_function  
1997 - (  
1998 - List(Web_Action($SessionTicket, $State)) actions  
1999 - ) =  
2000 - with f =  
2001 - (Maybe($State) previous,  
2002 - String action_name,  
2003 - HTTP_Info http_info,  
2004 - List(Web_arg) lwa,  
2005 - Bool is_https,  
2006 - List(Web_Action($SessionTicket, $State)) actions) |-f->  
2007 - if actions is  
2008 - {  
2009 - [ ] then (print("action '"+action_name+  
2010 - "' not found.\n"); (failure, previous, [])),  
2011 - [ac1 . others] then if ac1 is  
2012 - {  
2013 - http_action(an,allow,do_it) then  
2014 - if an = action_name  
2015 - then if is_https  
2016 - then (print("HTTP action '"+an+  
2017 - "' called through HTTPS (denied).\n");  
2018 - (failure, previous, []))  
2019 - else if allow(previous)  
2020 - then do_it(http_info,lwa,previous)  
2021 - else (failure, previous, [])  
2022 - else f(previous,action_name,http_info,lwa,is_https,others),  
2023 -  
2024 - https_action(an,allow,do_it) then  
2025 - if an = action_name  
2026 - then if is_https  
2027 - then if allow(previous)  
2028 - then do_it(http_info,lwa,previous)  
2029 - else (failure, previous, [])  
2030 - else (print("HTTPS action '"+an+  
2031 - "' called through HTTP (denied).\n");  
2032 - (failure, previous, []))  
2033 - else f(previous,action_name,http_info,lwa,is_https,others),  
2034 -  
2035 - http_https_action(an,allow,do_it) then  
2036 - if an = action_name  
2037 - then if allow(previous)  
2038 - then do_it(http_info,lwa,previous)  
2039 - else (failure, previous, [])  
2040 - else f(previous,action_name,http_info,lwa,is_https,others),  
2041 -  
2042 - }  
2043 - },  
2044 - (Maybe($State) previous,  
2045 - String action_name,  
2046 - HTTP_Info http_info,  
2047 - List(Web_arg) lwa,  
2048 - Bool is_https) |->  
2049 - f(previous,action_name,http_info,lwa,is_https,actions).  
2050 -  
2051 -  
2052 -  
2053 -  
2054 -  
2055 -  
2056 -  
2057 -  
2058 - *** [4] Web site descriptions and the 'awp handlers'.  
2059 -  
2060 - *** [4.1] The type 'Web_Site'.  
2061 -  
2062 - The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We  
2063 - need another one, because, we have some extra informations to record for each site.  
2064 -  
2065 -public type Web_Site:  
2066 - web_site((Int32,Int32) -> Web_Site_Description description,  
2067 - List(String) -> One delete_out_of_date).  
2068 -  
2069 -  
2070 -  
2071 -  
2072 - *** [4.2] Making a web site description.  
2073 -  
2074 - Below is the function which creates a web site description. It first creates (if  
2075 - needed) the directories for the site, then constructs the tool functions for the site,  
2076 - and the site handler. Finally, it constructs the web site description.  
2077 -  
2078 - We gather common (constant) informations in the following type:  
2079 -  
2080 -type CommonInfo:  
2081 - info(String common_name,  
2082 - Int32 http_port,  
2083 - Int32 https_port,  
2084 - String site_directory,  
2085 - String authorization_secret  
2086 - ).  
2087 -  
2088 - We need a forward declaration.  
2089 -  
2090 -public define Printable_tree  
2091 - format  
2092 - (  
2093 - CommonInfo cinfo,  
2094 - String state_name,  
2095 - HTML_Page page,  
2096 - Bool is_https,  
2097 - String charset  
2098 - ).  
2099 -  
2100 -  
2101 -define Printable_tree  
2102 - format  
2103 - (  
2104 - HTML_Size s  
2105 - ) =  
2106 - if s is  
2107 - {  
2108 - absolute(x) then ["\"",x,"\""],  
2109 - percentage(x) then ["\"",x,"%\""]  
2110 - }.  
2111 -  
2112 -define Printable_tree  
2113 - top_redirection_page  
2114 - (  
2115 - String common_name  
2116 - ) =  
2117 - [ doctype_w3c_header,  
2118 - "<html><head>",  
2119 - "<meta http-equiv=\"Refresh\" content=\"0; URL=javascript:void window.open('/?t=','_top')\" />",  
2120 - "</head><body></body></html>"  
2121 - ].  
2122 -  
2123 -public define Web_Site  
2124 - make_web_site_description  
2125 - (  
2126 - List(String) common_names, // for example: ["www.our-business.com"]  
2127 - String site_directory,  
2128 - String state_directory,  
2129 - One -> One init,  
2130 - (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,  
2131 - ($State expired,  
2132 - HTTP_Info,  
2133 - List(Web_arg),  
2134 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,  
2135 - (HTTP_Info,  
2136 - List(Web_arg),  
2137 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,  
2138 - List(Web_Action($SessionTicket, $State)) actions,  
2139 - (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,  
2140 - Int32 timeout,  
2141 - List(Redirection) redirections,  
2142 - String charset,  
2143 - List(String) journal_extensions,  
2144 - List(String) journal_headers,  
2145 - String secret,  
2146 - List(MIME) known_mime_types,  
2147 - (String action_name,  
2148 - List(Web_arg) args) -> One before_send_file  
2149 - ) =  
2150 - init(unique);  
2151 -  
2152 -  
2153 - //  
2154 - // make required directories (if needed)  
2155 - //  
2156 - with web_sites_directory = make_directory(my_anubis_directory+"/web_sites"),  
2157 - base_directory = make_directory(site_directory),  
2158 - // state_directory = make_directory(site_directory+"/states"),  
2159 - forget(make_directory(site_directory+"/public"));  
2160 - //  
2161 - // construct tool functions  
2162 - //  
2163 - with save_state = make_save_state_function(timeout,state_directory),  
2164 - retrieve_state = make_retrieve_state_function(state_directory),  
2165 - separate_web_args = make_separate_web_args_function(state_directory,retrieve_state),  
2166 - apply_action = make_apply_action_function(actions),  
2167 - //  
2168 - // construct the site handler  
2169 - //  
2170 - site_handler = (Int32 http_port, Int32 https_port) |->  
2171 - ((String host_name,  
2172 - HTTP_Info http_info,  
2173 - List(Web_arg) lwa,  
2174 - Bool is_https) |->  
2175 - ((List(HTTP_header),Printable_tree))  
2176 - if separate_web_args(lwa) is  
2177 - {  
2178 - swa(mb_previous_state,mb_action_name,operands) then  
2179 - with state_and_headers = if mb_previous_state is  
2180 - {  
2181 - failure then  
2182 - if mb_action_name is  
2183 - {  
2184 - failure then initial_state(http_info),  
2185 - success(action_name) then  
2186 - apply_action(failure,action_name,http_info,operands,is_https)  
2187 - },  
2188 - success(previous_state) then if previous_state is  
2189 - {  
2190 - not_found then  
2191 - if mb_action_name is  
2192 - {  
2193 - failure then initial_state(http_info),  
2194 - success(_) then  
2195 - ticket_lost_state(http_info,lwa,is_https)  
2196 - },  
2197 -  
2198 - out_of_date(state) then  
2199 - ticket_expired_state(state,http_info,lwa,is_https),  
2200 -  
2201 - still_valid(state) then  
2202 - if mb_action_name is  
2203 - {  
2204 - failure then (failure, success(state), []),  
2205 - success(action_name) then  
2206 - apply_action(success(state),action_name,http_info,operands,is_https)  
2207 - }  
2208 - }  
2209 - },  
2210 - if state_and_headers is (session_ticket, mb_new_state, headers) then  
2211 - with state_name = save_state(mb_new_state),  
2212 - (headers,  
2213 - format(info(host_name, http_port, https_port, site_directory, secret),  
2214 - state_name,  
2215 - compute_page(session_ticket, mb_new_state),  
2216 - is_https,  
2217 - charset))  
2218 - }),  
2219 - //  
2220 - // make the delete_out_of_date function  
2221 - //  
2222 - delete_out_of_date =  
2223 - make_delete_out_of_date_states_function((Maybe($State))failure,  
2224 - site_directory+"/states"),  
2225 - //  
2226 - // construct the web site description  
2227 - //  
2228 - web_site((Int32 http_port, Int32 https_port) |->  
2229 - web_site_description(common_names,  
2230 - site_directory,  
2231 - redirections,  
2232 - charset,  
2233 - journal_extensions,  
2234 - journal_headers,  
2235 - secret,  
2236 - known_mime_types,  
2237 - site_handler(http_port,https_port),  
2238 - (List(Web_arg) lwa) |-> if separate_web_args(lwa) is  
2239 - swa(mb_previous_state,mb_action_name,operands) then  
2240 - if mb_action_name is  
2241 - {  
2242 - failure then unique  
2243 - success(an) then before_send_file(an,operands)  
2244 - }),  
2245 - delete_out_of_date).  
2246 -  
2247 -  
2248 -  
2249 -  
2250 - *** [4.3] Starting the servers.  
2251 -  
2252 -public define Start_Web_Sites_Result  
2253 - start_web_sites  
2254 - (  
2255 - Int32 ip_address, // the IP address shared by the web sites  
2256 - Int32 http_port, // usually: 80  
2257 - Int32 https_port, // usually: 443  
2258 - String ssl_certificate_common_name,  
2259 - List(Web_Site) web_sites, // web sites to be started  
2260 - Var(Bool) shutdown_required  
2261 - ) =  
2262 - with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),  
2263 - with http_server_r =  
2264 - start_http_server(ip_address,http_port,  
2265 - map(get_description,web_sites),  
2266 - load_denial_of_service_info),  
2267 - with https_server_r =  
2268 - start_https_server(ip_address,https_port,  
2269 - ssl_certificate_common_name,  
2270 - map(get_description,web_sites),  
2271 - load_denial_of_service_info),  
2272 - if http_server_r is ok(http_server)  
2273 - then  
2274 - (  
2275 - if https_server_r is ok(https_server)  
2276 - then  
2277 - (  
2278 - start_http_servers_tasks(map(get_description,web_sites),  
2279 - [http_server,https_server],  
2280 - 600); // period of 10 minutes  
2281 - delegate  
2282 - delete_states_loop(  
2283 - map((Web_Site ws) |->  
2284 - (site_directory(description(ws)(http_port,https_port))+  
2285 - "/states",delete_out_of_date(ws)),  
2286 - web_sites),  
2287 - 3600*24*3, // keep out of date states 3 days  
2288 - now,  
2289 - http_server,  
2290 - https_server,  
2291 - shutdown_required),  
2292 - ok(http_server,https_server)  
2293 - )  
2294 - else cannot_bind_to_port(https_port)  
2295 - )  
2296 - else  
2297 - (  
2298 - if https_server_r is ok(https_server)  
2299 - then cannot_bind_to_port(http_port)  
2300 - else cannot_bind_to_port(http_port,https_port)  
2301 - ).  
2302 -  
2303 -  
2304 -public define One  
2305 - start_web_sites  
2306 - (  
2307 - Int32 ip_address, // the IP address shared by the web sites  
2308 - Int32 http_port, // usually: 80  
2309 - Int32 https_port, // usually: 443  
2310 - String ssl_certificate_common_name,  
2311 - List(Web_Site) web_sites, // web sites to be started  
2312 - Var(Bool) shutdown_required,  
2313 - Logger log  
2314 - ) =  
2315 - if (Start_Web_Sites_Result)start_web_sites(ip_address,  
2316 - http_port,  
2317 - https_port,  
2318 - ssl_certificate_common_name,  
2319 - web_sites,  
2320 - shutdown_required) is  
2321 - {  
2322 - cannot_bind_to_port(n) then logError(log, "Cannot bind to port: "+n),  
2323 - cannot_bind_to_port(n,m) then logError(log, "Cannot bind to ports: "+n+", "+m),  
2324 - ok(s1,s2) then logInfo(log, "Servers started.")  
2325 - }.  
2326 -  
2327 -  
2328 -  
2329 -  
2330 -  
2331 - *** [5] HTML Formating.  
2332 -  
2333 - We need to translate HTML elements as defined above into actual HTML text.  
2334 -  
2335 - Actioners require special informations, which must be transmitted when needed by the  
2336 - 'format' functions:  
2337 -  
2338 - - the 'common name', which is used for URLs,  
2339 - - the HTTP/HTTPS port number,  
2340 - - the 'state name', which must be transmitted when the actioner is clicked upon,  
2341 - - the 'form name' (if any) to which the actioner refers.  
2342 -  
2343 - If the actioner is off form, and if it refers to a form, the name of that form is  
2344 - already known by the actioner. On the contrary, if the actioner is 'in form', it refers  
2345 - implicitly to the form containing it. The name of that form is transmitted to the  
2346 - 'format' functions called from within the formating of that form.  
2347 -  
2348 -  
2349 -  
2350 -  
2351 - *** [5.1] The type 'HTML_Any($T)'.  
2352 -  
2353 - The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The  
2354 - parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.  
2355 -  
2356 -type HTML_Any($T):  
2357 - any_text (List(Text_Option), String the_text),  
2358 - any_preformated (List(Text_Option), String),  
2359 - any_paragraph (List(Text_Option), $T content),  
2360 - any_image (String url),  
2361 - any_image (String url, Int32 width, Int32 height),  
2362 - any_table (List(Table_Option), HTML_Header_Row($T), List(HTML_Row($T))),  
2363 - any_center ($T),  
2364 - any_mail_to (String email, $T element),  
2365 - any_scroller (Int32 width, Int32 height,  
2366 - Int32 content_width, Int32 content_height,  
2367 - $T content),  
2368 - any_fixed_size (HTML_Size width, HTML_Size height, $T content),  
2369 - any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),  
2370 - any_actioner (Actioner_Connection,  
2371 - Actioner_Target,  
2372 - Actioner_Aspect,  
2373 - String action_name,  
2374 - List((String,String)) extra_ops,  
2375 - List(Actioner_Local_Action),  
2376 - Maybe(String) form_name),  
2377 - any_foreign_link (List(Text_Option), String url),  
2378 - any_foreign_link (List(Text_Option), String url, String name),  
2379 - any_private_download (String abs_path, String name, String extra_ext,  
2380 - Maybe((String,List((String,String))))),  
2381 - any_div (List(CoreAttrs), $T element),  
2382 - any_div_empty (List(CoreAttrs)),  
2383 - any_coreattrs (List(CoreAttrs)).  
2384 -  
2385 -  
2386 -  
2387 - *** [5.2] Formating a color.  
2388 -  
2389 - RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters  
2390 - hexadecimal values.  
2391 -  
2392 -define String  
2393 - html_format  
2394 - (  
2395 - RGB color  
2396 - ) =  
2397 - if color is rgb(r,g,b) then  
2398 - "#" + hexadecimal(word8_to_int32(r),2)  
2399 - + hexadecimal(word8_to_int32(g),2)  
2400 - + hexadecimal(word8_to_int32(b),2).  
2401 -  
2402 -  
2403 - The following is a very arbitrary definition of the opposite color. The thing which is  
2404 - important is that it is far from the original, so that characters in 'opposite' color  
2405 - are clearly visible over the original.  
2406 -  
2407 -define RGB  
2408 - opposite  
2409 - (  
2410 - RGB color  
2411 - ) =  
2412 - if color is rgb(r,g,b) then  
2413 - with r1 = word8_to_int32(r),  
2414 - with g1 = word8_to_int32(g),  
2415 - with b1 = word8_to_int32(b),  
2416 - rgb(truncate_to_word8(255-r1),  
2417 - truncate_to_word8(255-g1),  
2418 - truncate_to_word8(255-b1)).  
2419 -  
2420 -  
2421 -  
2422 -  
2423 - *** [5.3] Creating buttons.  
2424 -  
2425 - We want to be able to create buttons in the form of a pair of images (rollovers)  
2426 - automatically. We use the JPEG interface, because for the time being Anubis cannot  
2427 - handle other kinds of images.  
2428 -  
2429 -  
2430 - Computing printed text length.  
2431 -  
2432 - define Int32  
2433 - printed_text_width  
2434 - (  
2435 - Word8 -> Int32 char_size,  
2436 - List(Word8) l  
2437 - ) =  
2438 - if l is  
2439 - {  
2440 - [] then (Int32) 0,  
2441 - [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)  
2442 - }.  
2443 -  
2444 - define Int32  
2445 - printed_text_width  
2446 - (  
2447 - SystemFont font,  
2448 - String s  
2449 - ) =  
2450 - printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),  
2451 - explode(s)).  
2452 -  
2453 -  
2454 -  
2455 - Converting RGB to RGBA.  
2456 -  
2457 -define RGBA  
2458 - to_rgba  
2459 - (  
2460 - RGB color  
2461 - ) =  
2462 - if color is rgb(r,g,b) then rgba(r,g,b,255).  
2463 -  
2464 -  
2465 - Drawing a 'relief'.  
2466 -  
2467 - define One  
2468 - draw_relief  
2469 - (  
2470 - RGBAImage dest,  
2471 - RGBA color,  
2472 - Int32 contrast,  
2473 - Int32 x,  
2474 - Int32 y,  
2475 - Int32 width,  
2476 - Int32 height  
2477 - ) =  
2478 - with l = lighten(color,contrast),  
2479 - d = darken(color,contrast),  
2480 - draw_rectangle(dest,rect(x,y,x+width,y+1),l);  
2481 - draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);  
2482 - draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);  
2483 - draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).  
2484 -  
2485 -  
2486 - Creating a button background.  
2487 -  
2488 - define RGBAImage  
2489 - create_button_background  
2490 - (  
2491 - RGBA color,  
2492 - Int32 width,  
2493 - Int32 height  
2494 - ) =  
2495 - with result = create_rgba_image(width,height,color),  
2496 - draw_relief(result,color,100,0,0,width,height);  
2497 - draw_relief(result,color,70,1,1,width-2,height-2);  
2498 - draw_relief(result,color,55,2,2,width-4,height-4);  
2499 - draw_relief(result,color,35,3,3,width-6,height-6);  
2500 - draw_relief(result,color,20,4,4,width-8,height-8);  
2501 - draw_relief(result,color,10,5,5,width-10,height-10);  
2502 - draw_relief(result,color,5,6,6,width-12,height-12);  
2503 - result.  
2504 -  
2505 -  
2506 - Drawing the text over the background.  
2507 -  
2508 - define One  
2509 - draw_button_text  
2510 - (  
2511 - RGBAImage image,  
2512 - String text,  
2513 - Int32 text_index,  
2514 - Int32 pixel_x,  
2515 - Int32 y,  
2516 - Rectangle clip,  
2517 - RGBA color,  
2518 - SystemFont font,  
2519 - ) =  
2520 - if nth(text_index,text) is  
2521 - {  
2522 - failure then unique,  
2523 - success(c) then  
2524 - with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),  
2525 - draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)  
2526 - }.  
2527 -  
2528 - define One  
2529 - draw_button_text  
2530 - (  
2531 - RGBAImage image,  
2532 - String text,  
2533 - Int32 text_width,  
2534 - RGBA light_color,  
2535 - RGBA dark_color,  
2536 - SystemFont font  
2537 - ) =  
2538 - with image_width = width(image),  
2539 - image_height = height(image),  
2540 - x_pos = (image_width-text_width)>>1,  
2541 - clip = rect(0,0,image_width,image_height),  
2542 - new_light_color = lighten(light_color,150),  
2543 - new_dark_color = darken(dark_color,40),  
2544 - draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);  
2545 - draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).  
2546 -  
2547 -  
2548 - The next function creates the two images for a button. The information given is the  
2549 - main color of the button, the text of the button and the minimal width (in pixels) of  
2550 - the button. The function does not create the button if the images already exist. The  
2551 - two images are stored in the directory 'site_directory/buttons'. The names of the files  
2552 - are of the form:  
2553 -  
2554 - bxxxx_off.jpg  
2555 - bxxxx_on.jpg  
2556 -  
2557 - where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like  
2558 - 'rm'), and where 'xxxx' is created from the given informations by the formula:  
2559 -  
2560 - xxxx = web_arg_encode(sha1((color,text,width)))  
2561 -  
2562 - Hence, distinct informations give distinct file names.  
2563 -  
2564 -  
2565 - define String // returns xxxx  
2566 - create_button_images  
2567 - (  
2568 - String site_directory,  
2569 - RGBA color,  
2570 - String text,  
2571 - Int32 width,  
2572 - SystemFont font  
2573 - ) =  
2574 - with xxxx = web_arg_encode(sha1((color,text,width))),  
2575 - buttons_dir = site_directory+"/public/buttons",  
2576 - off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",  
2577 - on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",  
2578 - if file_exists(on_filepath)  
2579 - then xxxx  
2580 - else with  
2581 - text_width = printed_text_width(font,text),  
2582 - button_width = max(width,text_width+12),  
2583 - button_height = (Int32)20,  
2584 - light_color = lighten(color,60),  
2585 - very_light_color = lighten(light_color,30),  
2586 - dark_color = darken(color,40),  
2587 - background_off =  
2588 - create_button_background(color,button_width,button_height),  
2589 - background_on =  
2590 - create_button_background(light_color,button_width,button_height),  
2591 -  
2592 - draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);  
2593 - draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);  
2594 - forget(write_image_to_JPEG_file(to_JPEG(background_off),  
2595 - off_filepath,  
2596 - 100));  
2597 - forget(write_image_to_JPEG_file(to_JPEG(background_on),  
2598 - on_filepath,  
2599 - 100));  
2600 - xxxx.  
2601 -  
2602 -  
2603 -  
2604 -  
2605 -  
2606 - *** [5.4] Formating an actioner.  
2607 -  
2608 - An actioner works as follows. Assume first that it refers to a form. When it is clicked  
2609 - upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the  
2610 - form, and submits the form, using the JavaScript command 'form_name.submit()'. If the  
2611 - actioner does not refer to a form, it fires the URL directly via 'href', because in  
2612 - that case, the actioner is always an <a> tag.  
2613 -  
2614 - The URL itself is composed using the connection sort (same, http or https), the common  
2615 - name and port number (if needed), the state name, the action name, and the extra  
2616 - operands, which are put into a query string. It may look like this:  
2617 -  
2618 - http://common_name:port/?s=state_name&a=action_name&oname=value...  
2619 -  
2620 - Each extra operand is a pair of strings: (name,value). It is formated as:  
2621 -  
2622 - &oname=value  
2623 -  
2624 -  
2625 -define String  
2626 - format_extra_operands  
2627 - (  
2628 - List((String,String)) l  
2629 - ) =  
2630 - if l is  
2631 - {  
2632 - [ ] then "",  
2633 - [h . t] then if h is (n,v) then  
2634 - "&amp;o"+n+"="+v+format_extra_operands(t)  
2635 - }.  
2636 -  
2637 -  
2638 - It seams that the standard requires "&amp;" instead of "&" !  
2639 -  
2640 - In case the target is another window, we need to format the options for this window.  
2641 -  
2642 -define String  
2643 - format  
2644 - (  
2645 - List(Other_Window_Option) l  
2646 - ) =  
2647 - if l is  
2648 - {  
2649 - [ ] then "",  
2650 - [h . t] then if h is  
2651 - {  
2652 - resizable then "resizable",  
2653 - scrollbars then "scrollbars",  
2654 - width(w) then "width="+w,  
2655 - height(h) then "height="+h  
2656 - } + if t is [ ] then "" else (","+format(t))  
2657 - }.  
2658 -  
2659 -  
2660 -  
2661 - Formating choices for a <select> tag.  
2662 -  
2663 -public define Printable_tree  
2664 - format_choices  
2665 - (  
2666 - List(String) l  
2667 - ) =  
2668 - if l is  
2669 - {  
2670 - [ ] then [ ],  
2671 - [h . t] then ["<option>", h, "</option>\n" . format_choices(t)]  
2672 - }.  
2673 -  
2674 -  
2675 -public define Printable_tree  
2676 - format_choices  
2677 - (  
2678 - List(String) l,  
2679 - InitialValue selected  
2680 - ) =  
2681 - if l is  
2682 - {  
2683 - [ ] then [ ],  
2684 - [h . t] then if h = selected.value  
2685 - then ["<option selected>", h, "</option>\n" . format_choices(t)]  
2686 - else ["<option>", h, "</option>\n" . format_choices(t,selected)]  
2687 - }.  
2688 -  
2689 -  
2690 -public define Printable_tree  
2691 - format_choices  
2692 - (  
2693 - List((WebArgValue,String)) l  
2694 - ) =  
2695 - if l is  
2696 - {  
2697 - [ ] then [ ],  
2698 - [h . t] then  
2699 - if h is (val,item)  
2700 - then ["<option value=\""+val.value+"\">", item, "</option>\n" . format_choices(t)]  
2701 - }.  
2702 -  
2703 -public define Printable_tree  
2704 - format_choices  
2705 - (  
2706 - List((WebArgValue,String)) l,  
2707 - InitialValue selected  
2708 - ) =  
2709 - if l is  
2710 - {  
2711 - [ ] then [ ],  
2712 - [h . t] then  
2713 - if h is (val,item) then  
2714 - if val.value = selected.value  
2715 - then ["<option value=\""+val.value+"\" selected=\"selected\">", item, "</option>\n" . format_choices(t)]  
2716 - else ["<option value=\""+val.value+"\">", item, "</option>\n" . format_choices(t,selected)]  
2717 - }.  
2718 -  
2719 -  
2720 -  
2721 -variable Int32 count = 0.  
2722 -  
2723 - Note: this counter is private to the virtual machine, hence there is one counter by  
2724 - client and by page : it seems that a new VM (simply a delegate) is started for each request.  
2725 -  
2726 -define Int32  
2727 - new_count  
2728 - =  
2729 - count <- *count+1;  
2730 - *count.  
2731 -  
2732 -  
2733 - The next function composes the URL. It is a JavaScript URL when the target is another  
2734 - window.  
2735 -  
2736 -define String  
2737 - make_actioner_url  
2738 - (  
2739 - CommonInfo cinfo,  
2740 - Actioner_Connection connection,  
2741 - Actioner_Target target,  
2742 - String state_name,  
2743 - String action_name,  
2744 - List((String,String)) extra_ops,  
2745 - Bool is_https  
2746 - ) =  
2747 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
2748 - with strict_url =  
2749 - if connection is  
2750 - {  
2751 - same then "/",  
2752 - /*  
2753 - same then if is_https  
2754 - then "https://"+common_name+":"+https_port+"/"  
2755 - else "http://"+common_name+":"+https_port+"/",  
2756 - */  
2757 -  
2758 - http then "http://"+common_name+":"+http_port+"/",  
2759 - https then "https://"+common_name+":"+https_port+"/",  
2760 - } +  
2761 - "?s=" + state_name + "&amp;a=" + action_name +  
2762 - format_extra_operands(extra_ops),  
2763 - if target is  
2764 - {  
2765 - same then strict_url,  
2766 - same(label) then strict_url+"#"+label,  
2767 - other(wn,ops) then  
2768 - "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+  
2769 - "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"  
2770 - }.  
2771 -  
2772 -  
2773 - Depending on the fact that the actioner refers to a form or not, the URL is used in two  
2774 - different ways. If the actioner does not refer to a form, it is realized by a '<a>'  
2775 - tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'  
2776 - tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'  
2777 - event handler. The handler calls a JavaScript function which puts the URL as the value  
2778 - of the 'action' attribute of the form, and submits the form.  
2779 -  
2780 -type URL_or_JavaScript:  
2781 - url (String),  
2782 - javascript (Printable_tree script, Printable_tree handler).  
2783 -  
2784 -  
2785 - It was as shown below, in order to allow submission of a form from outside the form,  
2786 - but this makes problems:  
2787 -  
2788 -define URL_or_JavaScript  
2789 - format_action  
2790 - (  
2791 - String the_url,  
2792 - Maybe(String) mb_id,  
2793 - Maybe(String) mb_form_name  
2794 - ) =  
2795 - if mb_form_name is  
2796 - {  
2797 - failure then  
2798 - url(the_url),  
2799 - success(form_name) then  
2800 - with n = new_count,  
2801 - fn_name = "pfu_" + if mb_id is  
2802 - {  
2803 - failure then form_name + n,  
2804 - success(id) then id,  
2805 - },  
2806 - javascript(  
2807 - [  
2808 - "<script type=\"text/javascript\" language =\"JavaScript\">\n",  
2809 - "function ", fn_name, "() {\n",  
2810 - " var fa = document.forms.f",form_name,";\n",  
2811 - " var u = \"",the_url,"\";\n",  
2812 - " fa.action = u;\n",  
2813 - " fa.submit(); }",  
2814 - "</script>\n"  
2815 - ],  
2816 - [fn_name,"();"]  
2817 - )  
2818 - }.  
2819 -  
2820 -  
2821 -  
2822 - Now, we format the actioner according to its aspect.  
2823 -  
2824 -define List(Text_Option)  
2825 - get_text_options  
2826 - (  
2827 - List(Text_Option) l  
2828 - )  
2829 - =  
2830 - if l is  
2831 - {  
2832 - [] then [],  
2833 - [h . t ] then  
2834 - if h is class(_) then  
2835 - get_text_options(t)  
2836 - else if h is id(_) then  
2837 - get_text_options(t)  
2838 - else  
2839 - [ h . get_text_options(t) ]  
2840 -  
2841 - }.  
2842 -  
2843 - /**  
2844 - * Extract the CSS class list from the list of Text_Option  
2845 - */  
2846 -define List(CoreAttrs)  
2847 - get_css_class  
2848 - (  
2849 - List(Text_Option) l  
2850 - )  
2851 - =  
2852 - if l is  
2853 - {  
2854 - [] then [],  
2855 - [h . t ] then  
2856 -  
2857 -  
2858 - if h is class(name) then  
2859 - [ class(name) . get_css_class(t) ]  
2860 - else if h is id(name) then  
2861 - [ id(name) . get_css_class(t) ]  
2862 - else  
2863 - get_css_class(t)  
2864 - }  
2865 - .  
2866 -  
2867 -define Printable_tree  
2868 - format  
2869 - (  
2870 - List(Actioner_Local_Action) l  
2871 - ) =  
2872 - if l is  
2873 - {  
2874 - [ ] then [ ],  
2875 - [h . t] then [if h is  
2876 - {  
2877 - close_window then [" window.close(); "]  
2878 - }  
2879 - . format(t)]  
2880 - }.  
2881 -  
2882 -public define String  
2883 - format_attrs  
2884 - (  
2885 - List(CoreAttrs) attributs  
2886 - )=  
2887 - if attributs is  
2888 - {  
2889 - [] then "",  
2890 - [h .t] then  
2891 - with current = if h is  
2892 - {  
2893 - id(id_name) then  
2894 - " id=\"" + id_name + "\"",  
2895 - class(class_name) then  
2896 - " class=\"" + class_name + "\"",  
2897 - style(style_string) then  
2898 - " style=\"" + style_string + "\"",  
2899 -  
2900 - tooltip(title_string) then  
2901 - " title=\"" + title_string + "\"",  
2902 -  
2903 - lang(lang) then  
2904 - " xml:lang=" + lang,  
2905 - dir(reading_Way) then  
2906 - if reading_Way is  
2907 - {  
2908 - ltr then " dir=ltr",  
2909 - rtl then " dir=rtl"  
2910 - },  
2911 -  
2912 - accesskey(key) then  
2913 - " accesskey=\"" + key + "\"",  
2914 - tabindex(index) then  
2915 - " tabindex=\"" + index + "\"",  
2916 -  
2917 - attr(name, value) then  
2918 - " " + name + "=\"" + value + "\"",  
2919 -  
2920 - event(e, value) then  
2921 - " " + event_name(e) + "=\"" + value + "\"",  
2922 - },  
2923 - current + format_attrs(t)  
2924 - }.  
2925 -  
2926 -define String  
2927 - format_attrs  
2928 - (  
2929 - List(InputAttrs) attributs  
2930 - )=  
2931 - if attributs is  
2932 - {  
2933 - [] then "",  
2934 - [h .t] then  
2935 - with current = if h is  
2936 - {  
2937 - class(class_name) then  
2938 - " class=\"" + class_name + "\"",  
2939 - style(style_string) then  
2940 - " style=\"" + style_string + "\"",  
2941 -  
2942 - tooltip(title_string) then  
2943 - " title=\"" + title_string + "\"",  
2944 -  
2945 - lang(lang) then  
2946 - " xml:lang=" + lang,  
2947 - dir(reading_Way) then  
2948 - if reading_Way is  
2949 - {  
2950 - ltr then " dir=ltr",  
2951 - rtl then " dir=rtl"  
2952 - },  
2953 -  
2954 - accesskey(key) then  
2955 - " accesskey=\"" + key + "\"",  
2956 - tabindex(index) then  
2957 - " tabindex=\"" + index + "\"",  
2958 -  
2959 - attr(name, value) then  
2960 - " " + name + "=\"" + value + "\"",  
2961 -  
2962 - event(e, value) then  
2963 - " " + event_name(e) + "=\"" + value + "\"",  
2964 - },  
2965 - current + format_attrs(t)  
2966 - }.  
2967 -  
2968 -define String  
2969 - format_text_options  
2970 - (  
2971 - List(Text_Option) l  
2972 - )  
2973 - =  
2974 - with text_options = get_text_options(l),  
2975 - css_classes = get_css_class(l),  
2976 - if text_options is  
2977 - {  
2978 - [] then "",  
2979 - [_._] then " style=\"" + format(text_options) + "\" "  
2980 - }  
2981 - +  
2982 - if css_classes is  
2983 - {  
2984 - [] then "",  
2985 - [_._] then format_attrs(css_classes)  
2986 - }.  
2987 -  
2988 -//define String  
2989 -// _format  
2990 -// (  
2991 -// List(CoreAttrs) opt  
2992 -// )=  
2993 -// if opt is  
2994 -// {  
2995 -// [] then "",  
2996 -// [h .t] then  
2997 -// with current = if h is  
2998 -// {  
2999 -// id(id_name) then  
3000 -// " id=\"" + id_name + "\"",  
3001 -// class(class_name) then  
3002 -// " class=\"" + class_name + "\"",  
3003 -// style(style_string) then  
3004 -// " style=\"" + style_string + "\"",  
3005 -//  
3006 -// title(title_string) then  
3007 -// " title=\"" + title_string + "\"",  
3008 -// lang(lang) then  
3009 -// " xml:lang=" + lang,  
3010 -// dir(reading_Way) then  
3011 -// if reading_Way is  
3012 -// {  
3013 -// ltr then " dir=ltr",  
3014 -// rtl then " dir=rtl"  
3015 -// },  
3016 -// attr(name, value) then  
3017 -// " " + name + "=\"" + value + "\"",  
3018 -// event(e, value) then  
3019 -// " " + event_name(e) + "=\"" + value + "\""  
3020 -// },  
3021 -// current + _format(t)  
3022 -// }  
3023 -// .  
3024 -  
3025 -define Printable_tree  
3026 - format_div_option  
3027 - (  
3028 - List(CoreAttrs) options  
3029 - )=  
3030 - ["<div" + format_attrs(options) + ">"] .  
3031 -  
3032 -  
3033 -define Maybe(String)  
3034 - extract_id  
3035 - (  
3036 - List(CoreAttrs) attrbs  
3037 - ) =  
3038 - if attrbs is  
3039 - {  
3040 - [] then failure,  
3041 - [h . t] then  
3042 - if h is id(value) then success(value)  
3043 - else extract_id(t)  
3044 - }.  
3045 -  
3046 -define Maybe(String)  
3047 - extract_id  
3048 - (  
3049 - Actioner_Aspect aspect  
3050 - ) =  
3051 - if aspect is  
3052 - {  
3053 - link(_,_) then failure,  
3054 - push_button(opts,_) then extract_id(opts),  
3055 - submit(opts,_) then extract_id(opts),  
3056 - button(_,_) then failure,  
3057 - button(_,_,_,_) then failure,  
3058 - immediate_selector(_,_,_) then failure,  
3059 - immediate_selector(_,_,_,_) then failure,  
3060 - }.  
3061 -  
3062 -define Printable_tree  
3063 - format_actioner  
3064 - (  
3065 - CommonInfo cinfo,  
3066 - String state_name,  
3067 - Actioner_Connection connection,  
3068 - Actioner_Target target,  
3069 - Actioner_Aspect aspect,  
3070 - String action_name,  
3071 - List((String,String)) extra_ops,  
3072 - List(Actioner_Local_Action) local_actions,  
3073 - Maybe(String) mb_form_name,  
3074 - Bool is_https,  
3075 - ) =  
3076 - if cinfo is info(common_name,http_port,https_port,site_dir,secret) then  
3077 - with url = make_actioner_url(cinfo,connection,target,  
3078 - state_name,action_name,extra_ops,is_https),  
3079 - with action = format_action(url, extract_id(aspect), mb_form_name),  
3080 - if aspect is  
3081 - {  
3082 -  
3083 - link(opt, text) then [  
3084 - if action is  
3085 - {  
3086 - url(u) then  
3087 - ["<a href=\"",u,"\"", format_text_options(opt), ">"],  
3088 -// ["<a href=\"",u,"\" style=\"",format(reverse(opt)),"\">"],  
3089 - javascript(s,h) then  
3090 - [s,"<a href=\"javascript: ",h,"\">"]  
3091 - },  
3092 - text,  
3093 - "</a>"  
3094 - ],  
3095 -  
3096 - push_button(options, text) then  
3097 - [  
3098 - if action is  
3099 - {  
3100 - url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",  
3101 - text, "</a>"  
3102 - ],  
3103 - javascript(s,h) then  
3104 - [s,"<input type=\"button\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]  
3105 - }  
3106 - ],  
3107 - submit(options, text) then  
3108 - [  
3109 - if action is  
3110 - {  
3111 - url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",  
3112 - text, "</a>"  
3113 - ],  
3114 - javascript(s,h) then  
3115 - if action_name = "" then // special case where URL (so the action_name) is provided by the form itself  
3116 - // so javascript isn't needed. This is the standard HMTL way.  
3117 - ["<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " />"]  
3118 - else  
3119 - [s,"<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]  
3120 - }  
3121 - ],  
3122 -  
3123 - button(url_off,url_on) then  
3124 - [ if action is  
3125 - {  
3126 - url(u) then ["<a href=\"",u],  
3127 - javascript(s,h) then [s,"<a onmousedown=\"",h]  
3128 - },  
3129 - "\" style=\"text-decoration:none\">",  
3130 - "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=\"0\"",  
3131 - " onmouseover=\"this.src='",url_on,"'\" ",  
3132 - " onmouseout=\"this.src='",url_off,"'\">",  
3133 - "</a>"  
3134 - ],  
3135 -  
3136 - button(url_off,url_on,w,h) then  
3137 - [ if action is  
3138 - {  
3139 - url(u) then ["<a href=\"",u],  
3140 - javascript(s,h) then [s,"<a onmousedown=\"",h]  
3141 - },  
3142 - "\" style=\"text-decoration:none\">",  
3143 - "<img width=\"",w,"\" height=\"",h,"\" alt=\"",url_off,"\" src=\"",url_off,"\" border=\"0\"",  
3144 - " onmouseover=\"this.src='",url_on,"'\" ",  
3145 - " onmouseout=\"this.src='",url_off,"'\">",  
3146 - "</a>"  
3147 - ],  
3148 -  
3149 - immediate_selector(name,size,choices) then  
3150 - [ if action is  
3151 - {  
3152 - url(u) then ["<select href=\"",u]  
3153 - javascript(s,h) then [s,"<select onchange=\"",h]  
3154 - },  
3155 - "\" name=\"o",name,"\" size=\"",size,"\">",  
3156 - format_choices(choices),"</select>"  
3157 - ],  
3158 - immediate_selector(name,size,choices,selected) then  
3159 - [ if action is  
3160 - {  
3161 - url(u) then ["<select href=\"",u]  
3162 - javascript(s,h) then [s,"<select onchange=\"",h]  
3163 - },  
3164 - "\" name=\"o",name,"\" size=\"",size,"\">",  
3165 - format_choices(choices, selected),"</select>"  
3166 - ],  
3167 - }.  
3168 -  
3169 -  
3170 -define Printable_tree  
3171 - format_local_popup_button  
3172 - (  
3173 - CommonInfo cinfo,  
3174 - Actioner_Aspect aspect,  
3175 - Int32 n,  
3176 - ) =  
3177 - if cinfo is info(common_name,http_port,https_port,site_dir,secret) then  
3178 - [ "<script type=\"text/javascript\" language=\"JavaScript\">",  
3179 - " var lpust_",n," = new Array(); ",  
3180 - " lpust_",n,"[0] = 0; ",  
3181 - "</script>\n",  
3182 - "<a href=\"javascript:show_local_popup('lpu_",n,"','lpust_",n,"');\">",  
3183 - if aspect is  
3184 - {  
3185 - link(opt,text) then [text],  
3186 - push_button(opt, text) then [text],  
3187 - submit(opt, text) then [text],  
3188 - button(url_off,url_on) then  
3189 - [  
3190 - "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",  
3191 - " onMouseOver=\"this.src='",url_on,"'\" ",  
3192 - " onMouseOut=\"this.src='",url_off,"'\">",  
3193 - ],  
3194 -  
3195 - button(url_off,url_on,w,h) then  
3196 - [  
3197 - "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",  
3198 - " onMouseOver=\"this.src='",url_on,"'\" ",  
3199 - " onMouseOut=\"this.src='",url_off,"'\">",  
3200 - ],  
3201 -  
3202 - immediate_selector(name,size,choices) then alert,  
3203 - immediate_selector(name,size,choices,selected) then alert,  
3204 -  
3205 - },  
3206 - "</a>"].  
3207 -  
3208 -  
3209 -  
3210 -  
3211 -  
3212 - *** [5.5] Formating a private download link.  
3213 -  
3214 - We get the absolute path of the file to be downloaded, and the name under which it  
3215 - should appear to the client. The function 'format_private_download' creates an  
3216 - hypertext link for downloading the file. The secured mecanism of private download is  
3217 - used. This function is called by the function which formats HTML_Any($T) elements.  
3218 -  
3219 -  
3220 -define Printable_tree  
3221 - format_private_download  
3222 - (  
3223 - CommonInfo cinfo,  
3224 - String sn, // state name  
3225 - String abs_path, // absolute file path on server  
3226 - String name, // name of file as it appears in the browser  
3227 - String extra, // extra extension  
3228 - Maybe((String,List((String,String)))) action  
3229 -  
3230 - ) =  
3231 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3232 - with private_download_directory = site_directory+"/private_download",  
3233 - with auth = make_authorization(site_directory,secret,abs_path),  
3234 - [  
3235 - "<a href=\"",name,extra,"?zauth=",auth,  
3236 - if action is  
3237 - {  
3238 - failure then [ ]  
3239 - success(a) then if a is (an,args) then  
3240 - ["&amp;a=",an,format_extra_operands(args)]  
3241 - },  
3242 - "\">",  
3243 - name,  
3244 - "</a>"  
3245 - ].  
3246 -  
3247 -  
3248 -  
3249 -  
3250 - *** [5.6] Formating rows and cells in a table.  
3251 -  
3252 -define Int32  
3253 - percent  
3254 - (  
3255 - Int32 p  
3256 - ) =  
3257 - if p < 0 then 0 else if p > 100 then 100 else p.  
3258 -  
3259 -  
3260 -  
3261 -  
3262 - Formating cell options.  
3263 -  
3264 -  
3265 -  
3266 -define String  
3267 - format  
3268 - (  
3269 - BackgroundOption o  
3270 - ) =  
3271 - if o is  
3272 - {  
3273 - repeat then "",  
3274 - repeat_horizontal then "; background-repeat: repeat-x",  
3275 - repeat_vertical then "; background-repeat: repeat-y",  
3276 - no_repeat then "; background-repeat: no-repeat",  
3277 - center then "; background-position: center top"  
3278 - }.  
3279 -  
3280 -define String  
3281 - format  
3282 - (  
3283 - List(BackgroundOption) l  
3284 - ) =  
3285 - if l is  
3286 - {  
3287 - [ ] then "",  
3288 - [h . t] then format(h)+format(t)  
3289 - }.  
3290 -  
3291 -  
3292 -define String  
3293 - format  
3294 - (  
3295 - List(Cell_Option) options  
3296 - ) =  
3297 - if options is  
3298 - {  
3299 - [ ] then "",  
3300 - [h . t] then  
3301 - if h is  
3302 - {  
3303 - core_attrs(core_attr_list) then format_attrs(core_attr_list),  
3304 - left then " align=\"left\"",  
3305 - h_center then " align=\"center\"",  
3306 - right then " align=\"right\"",  
3307 - top then " valign=\"top\"",  
3308 - v_center then " valign=\"middle\"",  
3309 - bottom then " valign=\"bottom\"",  
3310 - base_line then " valign=\"baseline\"",  
3311 - background_color(c) then " bgcolor=\""+html_format(c)+"\"",  
3312 - background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",  
3313 - width(w) then " width=\""+w+"\"",  
3314 - percentage_width(n) then " width=\""+percent(n)+"%\"",  
3315 - height(h) then " height=\""+h+"\"",  
3316 - columns(n) then " colspan=\""+n+"\"",  
3317 - rows(n) then " rowspan=\""+n+"\"",  
3318 - nowrap then " nowrap"  
3319 - }  
3320 - + format(t)  
3321 - }.  
3322 -  
3323 -  
3324 - Normalizing a list of cell options (horizontal position must be specified; the default  
3325 - is 'left').  
3326 -  
3327 -define List(Cell_Option)  
3328 - normalize  
3329 - (  
3330 - List(Cell_Option) l  
3331 - ) =  
3332 - if member(l,left) then l else  
3333 - if member(l,h_center) then l else  
3334 - if member(l,right) then l else  
3335 - //[left . l]. CR: why ? there is no default because we can use CSS  
3336 - l.  
3337 -  
3338 -  
3339 - Formating cells in a row.  
3340 -  
3341 -define Printable_tree  
3342 - format  
3343 - (  
3344 - List(HTML_Cell($T)) cells,  
3345 - $T -> Printable_tree format_element  
3346 - ) =  
3347 - if cells is  
3348 - {  
3349 - [ ] then [ ],  
3350 - [h . t] then if h is cell(options,element) then  
3351 - ["<td ",format(reverse(normalize(options))),">",  
3352 - format_element(element),  
3353 - "</td>"  
3354 - . format(t,format_element)]  
3355 - }.  
3356 -  
3357 -define Printable_tree  
3358 - format  
3359 - (  
3360 - List(HTML_Header_Cell($T)) cells,  
3361 - $T -> Printable_tree format_element  
3362 - ) =  
3363 - if cells is  
3364 - {  
3365 - [ ] then [ ],  
3366 - [h . t] then if h is header_cell(options,element) then  
3367 - ["<th ",format(reverse(normalize(options))),">",  
3368 - format_element(element),  
3369 - "</th>"  
3370 - . format(t,format_element)]  
3371 - }.  
3372 -  
3373 - Formating the rows in a table.  
3374 -  
3375 -define Printable_tree  
3376 - format  
3377 - (  
3378 - List(HTML_Row($T)) rows,  
3379 - $T -> Printable_tree format_element,  
3380 - ) =  
3381 - if rows is  
3382 - {  
3383 - [ ] then [ ],  
3384 - [h . t] then if h is row(options,cells) then  
3385 - ["<tr ",format(reverse(options)),">",  
3386 - format(cells,format_element),  
3387 - "</tr>"  
3388 - . format(t,format_element)]  
3389 - }.  
3390 -  
3391 -define Printable_tree  
3392 - format  
3393 - (  
3394 - HTML_Header_Row($T) row,  
3395 - $T -> Printable_tree format_element  
3396 - ) =  
3397 - if row is  
3398 - {  
3399 - empty then [ ],  
3400 - header_row(options,cells) then  
3401 - ["<thead> <tr ",format(reverse(options)),">",  
3402 - format(cells,format_element),  
3403 - "</tr> </thead>"  
3404 - ]  
3405 - }.  
3406 -  
3407 -define Printable_tree  
3408 - format1  
3409 - (  
3410 - List(TextAreaOption) l  
3411 - ) =  
3412 - if l is  
3413 - {  
3414 - [] then [],  
3415 - [h . t] then if h is  
3416 - {  
3417 - disabled then [" disabled " . format1(t)]  
3418 - read_only then [" readonly " . format1(t)]  
3419 - wrap_lines then [" wrap " . format1(t)]  
3420 - }  
3421 - }.  
3422 -  
3423 -define Printable_tree  
3424 - format  
3425 - (  
3426 - List(TextAreaOption) l  
3427 - ) =  
3428 - if member(l,wrap_lines)  
3429 - then format1(l)  
3430 - else [" wrap=off " . format1(l)].  
3431 -  
3432 -  
3433 - *** [5.7] Formating elements which may be put anywhere.  
3434 -  
3435 - The function below involves the parameter $T which is later instantiated as  
3436 - 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions  
3437 - for these two types, and because formating of tables requires recursive calls of such  
3438 - functions, it is necessary to provide the 'format' function to be called recursively as  
3439 - an argument. Putting naively a call to 'format' will not work, because the compiler  
3440 - will look for a function able to format data of type $T (which is at that time distinct  
3441 - from any other type, including our two types). Such a function does not exist. Hence  
3442 - the function to be called for formating elements must be passed as a functional  
3443 - argument (called 'format_element' below). Actually, what we pass is a function taking  
3444 - a unique argument of type $T. Other informations (like the name of the state) are  
3445 - already in the function by way of full functionality.  
3446 -  
3447 -  
3448 - Formating text options. They are formated in CSS syntax, to be used within a  
3449 - 'style=...'.  
3450 -  
3451 -define String  
3452 - format  
3453 - (  
3454 - List(Text_Option) l  
3455 - ) =  
3456 - if l is  
3457 - {  
3458 - [ ] then "",  
3459 - [h . t] then if h is  
3460 - {  
3461 - size(n) then "font-size:"+n+"pt",  
3462 - font(fn) then "font-family:"+fn,  
3463 - color(c) then if c is rgb(r,g,b) then  
3464 - "color:rgb("+word8_to_int32(r)+","+word8_to_int32(g)+","+word8_to_int32(b)+")",  
3465 - italic then "font-style:italic",  
3466 - oblique then "font-style:oblique",  
3467 - small_capitals then "font-variant:small-caps",  
3468 - bold then "font-weight:bold",  
3469 - underlined then "text-decoration:underline",  
3470 - left_justified then "text-align:left",  
3471 - right_justified then "text-align:right",  
3472 - justified then "text-align:justify",  
3473 - line_through then "text-decoration:line-through",  
3474 - nowrap then "white-space:nowrap",  
3475 - class(class_name)then " class=\"" +class_name +"\"",  
3476 - id(id_name) then " id=\"" +id_name +"\""  
3477 - } + if t is [ ] then "" else ("; "+format(t))  
3478 - }.  
3479 -  
3480 -  
3481 -  
3482 - Formating table options.  
3483 -  
3484 -define String  
3485 - format  
3486 - (  
3487 - List(Table_Option) l,  
3488 - Bool border_seen  
3489 - ) =  
3490 - if l is  
3491 - {  
3492 - [ ] then if border_seen then "" else " border=\"0\" cellspacing=\"0\" cellpadding=\"0\"",  
3493 - [h . t] then if h is  
3494 - {  
3495 - background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),  
3496 - background_image(url) then " background="+url+format(t,border_seen),  
3497 - border(o,top,i,c) then " border=\""+o+"\" cellspacing=\""+top+"\" cellpadding=\""+i+"\""+  
3498 - //" bordercolor="+format(c)+  
3499 - format(t,true),  
3500 - width(w) then " width=\""+w+"\""+format(t,border_seen),  
3501 - percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),  
3502 - }  
3503 - }.  
3504 -  
3505 -  
3506 -  
3507 -  
3508 -  
3509 -define Printable_tree  
3510 - format_scroller  
3511 - (  
3512 - String sn,  
3513 - Int32 width,  
3514 - Int32 height,  
3515 - Int32 content_width,  
3516 - Int32 content_height,  
3517 - Int32 idnum, // identifying the scroller  
3518 - $T content,  
3519 - $T -> Printable_tree format_element  
3520 - ) =  
3521 - [  
3522 - "<script type = \"text/javascript\" language=\"JavaScript\">",  
3523 - "function doscroll_",idnum,"(dx,dy) {\n",  
3524 - " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",  
3525 - " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",  
3526 - idnum,"').style\"); } else\n",  
3527 - " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",  
3528 - " var x_",idnum," = parseInt(c_",idnum,".left);\n",  
3529 - " var y_",idnum," = parseInt(c_",idnum,".top);\n",  
3530 - " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",  
3531 - " { x_",idnum," += dx; }\n",  
3532 - " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",  
3533 - " { y_",idnum," += dy; }\n",  
3534 - " c_",idnum,".left = x_",idnum,";\n",  
3535 - " c_",idnum,".top = y_",idnum,";\n",  
3536 - " }\n",  
3537 - "</script>\n",  
3538 - "<table>",  
3539 - "<tbody>",  
3540 - "<tr>",  
3541 - "<td align=left valign=top",  
3542 - " width=",width,  
3543 - " height=",height,  
3544 - ">",  
3545 - "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",  
3546 - " clip:rect(0px ",width,"px ",height,"px 0px)\">",  
3547 - "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",  
3548 - format_element(content),  
3549 - "</div>",  
3550 - "</div>",  
3551 - "</td>",  
3552 - "<td valign=bottom>",  
3553 - "<table>",  
3554 - "<tbody>",  
3555 - "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onmousedown=\"doscroll_",  
3556 - idnum,"(0,20);\"></td></tr>",  
3557 - "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onmousedown=\"doscroll_",  
3558 - idnum,"(0,-20);\"></td></tr>",  
3559 - "</tbody>",  
3560 - "</table>",  
3561 - "</td>",  
3562 - "</tr>",  
3563 - (if content_width > width then  
3564 - [  
3565 - "<tr>",  
3566 - "<td align=right>",  
3567 - "<table>",  
3568 - "<tbody>",  
3569 - "<tr>",  
3570 - "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onmousedown=\"doscroll_",  
3571 - idnum,"(20,0);\"></td>",  
3572 - "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onmousedown=\"doscroll_",  
3573 - idnum,"(-20,0);\"></td>",  
3574 - "</tr>",  
3575 - "</tbody>",  
3576 - "</table>",  
3577 - "</td>",  
3578 - "</tr>",  
3579 - ] else [ ]),  
3580 - "</tbody>",  
3581 - "</table>"  
3582 - ].  
3583 -  
3584 -  
3585 -/*  
3586 - define Printable_tree  
3587 - popup_topbar  
3588 - (  
3589 - CommonInfo cinfo,  
3590 - String title,  
3591 - RGB color,  
3592 - Int32 width,  
3593 - ) =  
3594 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3595 - with xxxx = web_arg_encode(sha1((title,color,width))),  
3596 - path = site_directory+"/public/buttons/t"+xxxx+".jpg",  
3597 - result = (Printable_tree)["<img alt=\"button\" src=\"buttons/t"+xxxx+".jpg\">"],  
3598 - if file_exists(path) then result else  
3599 - with col = to_rgba(color),  
3600 - bg = create_button_background(col,width,20),  
3601 - very_light_color = lighten(col,70),  
3602 - dark_color = darken(col,40),  
3603 - title_width = printed_text_width(font,title),  
3604 - draw_button_text(bg,title,title_width,very_light_color,dark_color,font);  
3605 - forget(write_image_to_JPEG_file(to_JPEG(bg),path,100));  
3606 - result.  
3607 -  
3608 -  
3609 - define Printable_tree  
3610 - popup_close_button  
3611 - (  
3612 - CommonInfo cinfo,  
3613 - RGB color,  
3614 - String div_name,  
3615 - String state_var_name,  
3616 - ) =  
3617 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3618 - with xxxx = web_arg_encode(sha1(color)),  
3619 - path_on = site_directory+"/public/buttons/c"+xxxx+"_on.jpg",  
3620 - path_off = site_directory+"/public/buttons/c"+xxxx+"_off.jpg",  
3621 - result = (Printable_tree)["<img alt=\"button\" src=\"buttons/c"+xxxx+"_off.jpg\"",  
3622 - " onMouseOver=\"this.src='buttons/c"+xxxx+"_on.jpg'\"",  
3623 - " onMouseOut=\"this.src='buttons/c"+xxxx+"_off.jpg'\"",  
3624 - " onMouseDown=\"show_local_popup('",div_name,"','",state_var_name,"')\">"],  
3625 - if file_exists(path_on) then result else  
3626 - with col = to_rgba(color),  
3627 - title = "x",  
3628 - title_width = printed_text_width(font,title),  
3629 - bg_on = create_button_background(lighten(col,30),20,20),  
3630 - bg_off = create_button_background(col,20,20),  
3631 - very_light_color = lighten(col,70),  
3632 - dark_color = darken(col,40),  
3633 - draw_button_text(bg_on,title,title_width,very_light_color,dark_color,font);  
3634 - draw_button_text(bg_off,title,title_width,very_light_color,dark_color,font);  
3635 - forget(write_image_to_JPEG_file(to_JPEG(bg_on),path_on,100));  
3636 - forget(write_image_to_JPEG_file(to_JPEG(bg_off),path_off,100));  
3637 - result.  
3638 -  
3639 -  
3640 -*/  
3641 -  
3642 -  
3643 - // The function below formats a datum of type 'HTML_Any($T)'.  
3644 -  
3645 -define Printable_tree  
3646 - format  
3647 - (  
3648 - CommonInfo cinfo,  
3649 - String sn, // state_name  
3650 - Var(Int32) ic_v,  
3651 - HTML_Any($T) element,  
3652 - $T -> Printable_tree format_element, // able to format a datum of type $T  
3653 - Bool is_https,  
3654 - ) =  
3655 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3656 - if element is  
3657 - {  
3658 - any_text(opts,t) then  
3659 - ["<span ", format_text_options(opts), ">",t,"</span>"],  
3660 - any_preformated(opts,s) then  
3661 - ["<span ", format_text_options(opts), "><pre>",s,"</pre></span>"],  
3662 - //["<pre>",s,"</pre>"],  
3663 - any_paragraph(opts,e) then  
3664 - ["<p ", format_text_options(opts), ">",format_element(e),"</p>\n"],  
3665 - any_image(url) then  
3666 - ["<img alt=\"",url,"\" src=\"",url,"\">"],  
3667 - any_image(url,w,h) then  
3668 - ["<img alt=\"",url,"\" src=\"",url,"\" width=",w," height=",h," />"],  
3669 - any_table(opts,h_row, rows) then  
3670 - ["<table ",format(reverse(opts),false),">",  
3671 - format(h_row,format_element),  
3672 - "<tbody>",format(rows,format_element),"</tbody></table>\n"],  
3673 - any_center(e) then  
3674 - ["<center>",format_element(e),"</center>"],  
3675 - any_mail_to(email,elem) then  
3676 - ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],  
3677 - any_scroller(w,h,cw,ch,c) then  
3678 - format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),  
3679 - any_fixed_size(w,h,c) then  
3680 - with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),  
3681 - ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",  
3682 - "secondary document",  
3683 - "</object>"],  
3684 - any_fixed_size_2(w,h,fn) then  
3685 - with url = fn+"?zauth="+make_authorization(site_directory,secret,  
3686 - fn),  
3687 - ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",  
3688 - "secondary document",  
3689 - "</object>"],  
3690 - any_actioner(c,t,a,an,eo,ja,fn) then  
3691 - format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https),  
3692 - any_foreign_link(options,url) then  
3693 - ["<a href=\"",url,"\" ", format_text_options(options), " />"],  
3694 - any_foreign_link(options,url,name) then  
3695 - ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],  
3696 - any_private_download(url,name,extra_ext,action) then  
3697 - format_private_download(cinfo,sn,url,name,extra_ext,action),  
3698 - any_div(options, e) then  
3699 - [format_div_option(options), format_element(e),"</div>\n"],  
3700 - any_div_empty(options) then  
3701 - [format_div_option(options), "</div>\n"],  
3702 - any_coreattrs(attributs) then  
3703 - [format_attrs(attributs)]  
3704 - }.  
3705 -  
3706 -  
3707 -  
3708 -  
3709 - // *** [5.8] Formating 'in form' elements.  
3710 -  
3711 -  
3712 -  
3713 -  
3714 -  
3715 -define Printable_tree  
3716 - format  
3717 - (  
3718 - CommonInfo cinfo,  
3719 - String fn, // form_name  
3720 - String sn, // state_name  
3721 - Var(Int32) ic_v, // idnum counter variable  
3722 - HTML_In_Form element,  
3723 - Bool is_https,  
3724 - ) =  
3725 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3726 - with format_element = (HTML_In_Form e) |-> format(cinfo,fn,sn,ic_v,e,is_https),  
3727 - if element is  
3728 - {  
3729 - literal_pt(t) then t,  
3730 - literal(t) then [t],  
3731 - sequence(l) then flat(map(format_element,l))  
3732 - text(opts,t) then  
3733 - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),  
3734 - preformated(o,s) then  
3735 - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),  
3736 - paragraph(opts,t) then  
3737 - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),  
3738 - image(url) then  
3739 - format(cinfo,sn,ic_v,any_image(url),format_element,is_https),  
3740 - image(url,w,h) then  
3741 - format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),  
3742 - table(opts,header_row,rows) then  
3743 - format(cinfo,sn,ic_v,any_table(opts, header_row, rows),format_element,is_https),  
3744 - center(e) then  
3745 - format(cinfo,sn,ic_v,any_center(e),format_element,is_https),  
3746 - mail_to(a,e) then  
3747 - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),  
3748 - scroller(w,h,cw,ch,c) then  
3749 - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),  
3750 - actioner(c,t,a,an,eo,ja) then  
3751 - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),  
3752 - foreign_link(options,url) then  
3753 - format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https),  
3754 - foreign_link(options,url,name) then  
3755 - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),  
3756 - private_download(url,name,extra,action) then  
3757 - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),  
3758 - text_input(options, label_text, id, name, i, w) then  
3759 - [ "<label for=\"",id,"\">",label_text,"</label>",  
3760 - "<input type=\"text\" name=\"o",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],  
3761 - text_input_ro(options, label_text, id, name,i,w) then  
3762 - [ "<label for=\"",id,"\">",label_text,"</label>",  
3763 - "<input readonly=\"readonly\" type=\"text\" name=\"o",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],  
3764 - password_input(options, label_text, id, name,i,w) then  
3765 - [ "<label for=\"",id,"\">",label_text,"</label>",  
3766 - "<input type=\"password\" name=\"p",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],  
3767 - text_area(opts,n,i,w,h) then  
3768 - ["<textarea ",format(opts)," name=\"o",n,"\" cols=\"",w,"\" rows=\"",h,"\">",i,"</textarea>"],  
3769 - file_upload(label, id, n, w) then  
3770 - ["<label for=\"",id,"\">",label,"</label>",  
3771 - "<input type=\"file\" id=\"",id,"\" size=\"",w,"\" name=\"o",n,"\" />"],  
3772 - selector(opts, label, id, n,s,cs) then  
3773 - ["<label for=\"",id,"\">",label,"</label>",  
3774 - "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],  
3775 - selector(opts, label, id, n,s,cs,sd) then  
3776 - ["<label for=\"",id,"\">",label,"</label>",  
3777 - "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],  
3778 - selector_c(opts, label, id, n,s,cs) then  
3779 - ["<label for=\"",id,"\">",label,"</label>",  
3780 - "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],  
3781 - selector_c(opts, label, id, n,s,cs,sd) then  
3782 - ["<label for=\"",id,"\">",label,"</label>",  
3783 - "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],  
3784 -  
3785 - radio_button(options, label_text, id, n, v, c) then  
3786 - [ "<label for=\"",id,"\">",label_text,"</label>",  
3787 - "<input type=\"radio\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />"],  
3788 - radio_button_r(options, label_text, id, n, v, c) then  
3789 - [ "<input type=\"radio\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />",  
3790 - "<label for=\"",id,"\">",label_text,"</label>"],  
3791 - check_box(options, label_text, id, n, v, c) then  
3792 - [ if length(label_text) > 0 then ["<label for=\"",id,"\">",label_text,"</label>"] else [""],  
3793 - "<input type=\"checkbox\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />"]  
3794 - check_box_r(options, label_text, id, n, v, c) then  
3795 - [ "<input type=\"checkbox\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />",  
3796 - if length(label_text) > 0 then ["<label for=\"",id,"\">",label_text,"</label>"] else [""] ]  
3797 - div(options, e) then  
3798 - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),  
3799 - div_empty(options) then  
3800 - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),  
3801 - hidden(name, value) then  
3802 - ["<input type=\"hidden\" name=\"o",name,"\" value=\"",value,"\" />"],  
3803 -  
3804 - }.  
3805 -  
3806 -  
3807 -  
3808 -  
3809 -  
3810 -  
3811 - *** [5.9] Formating 'off form' elements.  
3812 -  
3813 - The encryption type 'multipart/form-data' is required for a form containing an upload.  
3814 -  
3815 -  
3816 -define Bool  
3817 - contains_an_upload  
3818 - (  
3819 - HTML_In_Form form_content  
3820 - ).  
3821 -  
3822 -define Bool  
3823 - contains_an_upload  
3824 - (  
3825 - HTML_Row(HTML_In_Form) row  
3826 - ) =  
3827 - mapor(contains_an_upload,  
3828 - map(content,cells(row))).  
3829 -  
3830 -  
3831 -define Bool  
3832 - contains_an_upload  
3833 - (  
3834 - HTML_In_Form form_content  
3835 - ) =  
3836 - if form_content is  
3837 - {  
3838 - literal_pt(t) then false,  
3839 - literal(t) then false,  
3840 - sequence(l) then mapor(contains_an_upload,l)  
3841 - text(o,t) then false,  
3842 - preformated(o,s) then false,  
3843 - paragraph(o,t) then false,  
3844 - image(u) then false,  
3845 - image(u,w,h) then false,  
3846 - table(o,_,rows) then mapor(contains_an_upload,rows),  
3847 - center(e) then contains_an_upload(e),  
3848 - mail_to(m,e) then false, // 'e' may but should not contain an upload  
3849 - scroller(w,h,cw,ch,e) then contains_an_upload(e),  
3850 - actioner(c,t,a,an,eo,ja) then false,  
3851 - foreign_link(o,u) then false,  
3852 - foreign_link(o,u,n) then false,  
3853 - private_download(p,n,e,a) then false,  
3854 - text_input(o,lt,l,n,i,w) then false,  
3855 - text_input_ro(o,lt,l,n,i,w) then false,  
3856 - password_input(o,lt,l,n,i,w) then false,  
3857 - text_area(o,n,i,w,h) then false,  
3858 - file_upload(l,i,n,w) then true,  
3859 - selector(o,l,i,n,s,c) then false,  
3860 - selector(o,l,i,n,s,c,p) then false,  
3861 - selector_c(o,l,i,n,s,c) then false,  
3862 - selector_c(o,l,i,n,s,c,p) then false,  
3863 - radio_button(o,_,_,n,v,c) then false,  
3864 - radio_button_r(o,_,_,n,v,c) then false,  
3865 - check_box(o,_,_,n,v,c) then false,  
3866 - check_box_r(o,_,_,n,v,c) then false,  
3867 - div(o,c) then contains_an_upload(c),  
3868 - div_empty(o) then false,  
3869 - hidden(_,_) then false  
3870 - }.  
3871 -  
3872 -define String  
3873 - enctype  
3874 - (  
3875 - HTML_In_Form form_content  
3876 - ) =  
3877 - if contains_an_upload(form_content)  
3878 - then " enctype=\"multipart/form-data\""  
3879 - else "".  
3880 -  
3881 -  
3882 -  
3883 -define Printable_tree  
3884 - format  
3885 - (  
3886 - CommonInfo cinfo,  
3887 - String sn, // state_name  
3888 - Var(Int32) ic_v, // 'idnum' counter variable  
3889 - HTML_Off_Form element,  
3890 - Bool is_https,  
3891 - ) =  
3892 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3893 - with format_element = (HTML_Off_Form e) |-> format(cinfo,sn,ic_v,e,is_https),  
3894 - if element is  
3895 - {  
3896 - literal_pt(t) then t,  
3897 - literal(t) then [t],  
3898 - sequence(l) then flat(map(format_element,l)),  
3899 - text(opts,t) then  
3900 - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),  
3901 - preformated(o,s) then  
3902 - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),  
3903 - paragraph(opts,t) then  
3904 - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),  
3905 - image(url) then  
3906 - format(cinfo,sn,ic_v,any_image(url),format_element,is_https),  
3907 - image(url,w,h) then  
3908 - format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),  
3909 - table(opts,header_row, rows) then  
3910 - format(cinfo,sn,ic_v,any_table(opts,header_row, rows),format_element,is_https),  
3911 - center(e) then  
3912 - format(cinfo,sn,ic_v,any_center(e),format_element,is_https),  
3913 - mail_to(a,e) then  
3914 - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),  
3915 - scroller(w,h,cw,ch,c) then  
3916 - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),  
3917 - fixed_size(w,h,c) then  
3918 - format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https),  
3919 - fixed_size_2(w,h,fn) then  
3920 - format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https),  
3921 - actioner(c,t,a,an,eo,ja) then  
3922 - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https),  
3923 - actioner(c,t,a,an,eo,ja,fn) then  
3924 - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),  
3925 - foreign_link(options,url) then  
3926 - format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https),  
3927 - foreign_link(options,url,name) then  
3928 - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),  
3929 - private_download(url,name,extra,action) then  
3930 - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),  
3931 - label(n) then ["<a name=\"",n,"\">"],  
3932 - form(fn,attributs, c) then  
3933 - [  
3934 - "<form name=\"f",fn,"\"",  
3935 - format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),  
3936 - " method=\"post\"",  
3937 - enctype(c),  
3938 - " action=\"http",  
3939 - if is_https then "s" else "",  
3940 - "://",common_name,":",http_port,"/\">",  
3941 - // action is set dynamically by  
3942 - // the actioner using JavaScript  
3943 - format(cinfo,fn,sn,ic_v,c,is_https),  
3944 - "</form>"  
3945 - ]  
3946 - form(fn,attributs, action_name, extra_ops, c) then  
3947 - with url = make_actioner_url(cinfo, same, same,  
3948 - sn, action_name, extra_ops, is_https),  
3949 - [  
3950 - "<form name=\"f",fn,"\"",  
3951 - format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),  
3952 - " method=\"post\"",  
3953 - enctype(c),  
3954 - " action=\"" + url + "\">",  
3955 -// " action=\"http",  
3956 -// if is_https then "s" else "",  
3957 -// "://",common_name,":",http_port,"/\">",  
3958 - // action is set dynamically by  
3959 - // the actioner using JavaScript  
3960 - format(cinfo,fn,sn,ic_v,c,is_https),  
3961 - "</form>"  
3962 - ]  
3963 - div(options, e) then  
3964 - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),  
3965 - div_empty(options) then  
3966 - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),  
3967 -  
3968 - }.  
3969 -  
3970 -  
3971 -  
3972 -  
3973 - *** [5.10] Formating meta-tags.  
3974 -  
3975 -define Printable_tree  
3976 - format_keywords  
3977 - (  
3978 - List(String) l  
3979 - ) =  
3980 - if l is  
3981 - {  
3982 - [] then [ ],  
3983 - [h . t] then if t is []  
3984 - then [h]  
3985 - else [h , ", " . format_keywords(t)]  
3986 - }.  
3987 -  
3988 -  
3989 -define Printable_tree  
3990 - format  
3991 - (  
3992 - CommonInfo cinfo,  
3993 - String state_name,  
3994 - HTML_Meta m,  
3995 - Bool is_https  
3996 - ) =  
3997 - if m is  
3998 - {  
3999 - keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\" />\n"],  
4000 - refresh(co,ta,an,delay) then  
4001 - ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",  
4002 - make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\" />\n"],  
4003 - meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\" />\n"],  
4004 - http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\" />\n"],  
4005 - generic_meta(l) then ["<meta ",  
4006 - flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],  
4007 - l)),  
4008 - " />\n"],  
4009 - literal(s) then [s]  
4010 - }.  
4011 -  
4012 -  
4013 -define Printable_tree  
4014 - format  
4015 - (  
4016 - CommonInfo cinfo,  
4017 - String state_name,  
4018 - List(HTML_Meta) metas,  
4019 - Bool is_https,  
4020 - String charset  
4021 - ) =  
4022 - if metas is  
4023 - {  
4024 - [] then [format(cinfo,state_name,http_equiv("content-type",  
4025 - "text/html; charset="+charset),is_https)],  
4026 - [h . t] then [format(cinfo,state_name,h,is_https)  
4027 - . format(cinfo,state_name,t,is_https,charset)]  
4028 - }.  
4029 -  
4030 -  
4031 -define Printable_tree  
4032 - format  
4033 - (  
4034 - Body_Option o  
4035 - ) =  
4036 - if o is  
4037 - {  
4038 - background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],  
4039 - background_image(n) then [" background=", n],  
4040 - background_image(n,o) then [" style=\"background: url(",n,")",format(o),"\""]  
4041 -  
4042 - }.  
4043 -  
4044 -  
4045 -  
4046 -define Printable_tree  
4047 - format  
4048 - (  
4049 - List(Body_Option) l  
4050 - ) =  
4051 - if l is  
4052 - {  
4053 - [ ] then [ ],  
4054 - [h . t] then [format(h) . format(t)]  
4055 - }.  
4056 -  
4057 -define Printable_tree  
4058 - add_css_files  
4059 - (  
4060 - List(CSS_File) l  
4061 - ) =  
4062 - if l is  
4063 - {  
4064 - [ ] then [ ],  
4065 - [h . t] then  
4066 - [ ["<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file_name(h) + "\" />\n" ]  
4067 - . add_css_files(t)]  
4068 - }.  
4069 -  
4070 -define Printable_tree  
4071 - add_js_files  
4072 - (  
4073 - List(JS_File) l  
4074 - ) =  
4075 - if l is  
4076 - {  
4077 - [ ] then [ ],  
4078 - [h . t] then  
4079 - [ ["<script src=\""+ file_name(h) +"\" type=\"text/javascript\"></script>\n" ]  
4080 - . add_js_files(t)]  
4081 - }.  
4082 -  
4083 -define Printable_tree  
4084 - add_css_styles  
4085 - (  
4086 - List(CSS_Style) css_styles  
4087 - ) =  
4088 -  
4089 - if css_styles is  
4090 - {  
4091 - [] then [],  
4092 - [_._] then [ "<style type=\"text/css\"><!--\n",  
4093 - format_css_styles(css_styles),  
4094 - " --></style>\n"  
4095 - ]  
4096 - }.  
4097 -  
4098 -define Printable_tree  
4099 - format  
4100 - (  
4101 - CommonInfo cinfo,  
4102 - String state_name,  
4103 - HTML_Page page,  
4104 - Bool is_https,  
4105 - String charset  
4106 - ) =  
4107 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
4108 - with ic_v = var((Int32)0),  
4109 - if page is  
4110 - {  
4111 - html_page(title,metas,css_styles, css_files, js_files, body) then  
4112 - if body is body(options,element) then  
4113 - [ doctype_w3c_header,  
4114 - "<html>\n",  
4115 - "<head>\n",  
4116 - add_css_styles(css_styles),  
4117 - add_css_files(css_files),  
4118 - add_js_files(js_files),  
4119 - "<link rel=\"shortcut icon\" href=\"favicon.ico\" />\n",  
4120 - "<script type = \"text/javascript\" language=\"JavaScript\">",  
4121 - " function show_local_popup(divname,stvname) {",  
4122 - " if (document.layers) { var d = eval(document.divname); } else\n",  
4123 - " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",  
4124 - " if (document.all) { var d = eval(document.all.divname.style)};\n",  
4125 - // " alert(typeof(eval(stvname))); ",  
4126 - " var s = eval(stvname); ",  
4127 - " if (s[0]==0) ",  
4128 - " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",  
4129 - " { s[0]=0; d.style.visibility = 'hidden'; }; }",  
4130 - "</script>\n",  
4131 - "<title>",title,"</title>\n", // put title  
4132 - format(cinfo,state_name,metas,is_https,charset), // format the metas  
4133 - "</head>\n",  
4134 - "<body ", format(options), ">", // format body options  
4135 - //"<center>",  
4136 - format(cinfo,state_name,ic_v,element,is_https),  
4137 - //"</center>",  
4138 - "</body>\n",  
4139 - "</html>"  
4140 - ]  
4141 - }.  
4142 -  
4143 -  
4144 -  
4145 -  
4146 -  
4147 - 1 +
  2 +
  3 + *Project* Anubis
  4 +
  5 + *Title* Making interactive Web sites.
  6 +
  7 + *Copyright* Copyright (c) Alain Prouté 2004-2005.
  8 + Copyright (c) Calexium 2007.
  9 +
  10 +
  11 + *Authors* Alain Prouté
  12 + David René
  13 +
  14 + *Revised* May 2007
  15 +
  16 +
  17 + *Overview*
  18 +
  19 + In this file we propose simple tools for making well structured interactive and secured
  20 + web sites.
  21 +
  22 +
  23 + ----------------------------------- Table of Contents ---------------------------------
  24 +
  25 + * (1) Structure of a web site.
  26 + ** (1.1) Three sorts of data.
  27 + ** (1.2) How requests are handled.
  28 + ** (1.3) What web pages are made of.
  29 + ** (1.4) Actions.
  30 + ** (1.5) States.
  31 +
  32 + * (2) Carrying on.
  33 + ** (2.1) Describing your web sites.
  34 + ** (2.2) Directories on the server's disk.
  35 + ** (2.3) Starting your web sites.
  36 +
  37 + * (3) The HTML interface.
  38 + ** (3.1) Types used by the HTML interface.
  39 + ** (3.2) ``in form'' versus ``off form''.
  40 + ** (3.3) Defining your own style.
  41 + ** (3.4) Actioners and forms.
  42 + ** (3.5) Local popup.
  43 +
  44 + ---------------------------------------------------------------------------------------
  45 +
  46 +
  47 +read tools/basis.anubis
  48 +read system/logger.anubis
  49 +read CXM_common.anubis
  50 +read CXM_multihost_http_server.anubis
  51 +read CXM_mime.anubis
  52 +
  53 +
  54 +
  55 + * (1) Structure of a web site.
  56 +
  57 + First of all we need to explain what a web site should be made of. Ideally, the
  58 + visitor (also called the 'client') should see the web site working as any other
  59 + interactive computer software. So, it should be clear that a 'session' (i.e. a visit
  60 + to the web site, including the consultation of several pages) is some kind of
  61 + conversation between the visitor and the web site, and that the web site should
  62 + maintain a 'current state' of this conversation. At each new request (click) from the
  63 + visitor, this state must be updated. This whole conversation is called a 'session' and
  64 + should not be confused with a single request.
  65 +
  66 +
  67 +
  68 + ** (1.1) Three sorts of data.
  69 +
  70 + All the data needed for putting a web site at work may be dispatched into three
  71 + categories:
  72 +
  73 + 1. Constant data (data that never change). These data may be hard coded into the
  74 + Anubis source files of the web site.
  75 +
  76 + 2. Permanent data (data which always exist independantly of the users connected to
  77 + the web site). These data are normally recorded into data bases.
  78 +
  79 + 3. Session data (data which depend on a particular visitor and which exist only
  80 + during the time he visits the web site). These data are stored into so-called
  81 + 'states'.
  82 +
  83 +
  84 + It is important to determine which data belongs to which category. This is part of your
  85 + design decisions.
  86 +
  87 +
  88 +
  89 + ** (1.2) How requests are handled.
  90 +
  91 + We want to separate the following two functionalities (which are used at each request
  92 + (click) during a single session):
  93 +
  94 + - computing the new state from the previous state and from the client request, and
  95 + updating the data base,
  96 +
  97 + - computing the page to be sent to the client from the new current state and from
  98 + the informations in the data base.
  99 +
  100 +
  101 + The next picture shows the structure we have in mind:
  102 +
  103 +
  104 + request +---------+ HTML page (with a hidden state name)
  105 + .-------------------| client |<--------------.
  106 + | .-----------------| | |
  107 + | | previous state +---------+ |
  108 + | | name (if any) |
  109 + | | | client side
  110 + ............................................................................
  111 + | | | server side
  112 + | | |
  113 + | | .-------------------. |
  114 + | | | previous state | |
  115 + V V V | |
  116 + +---------------+ +---------------+ +--------------+
  117 + | compute state | | server's disk | | compute page |
  118 + +---------------+ +---------------+ +--------------+
  119 + ^ | | ^ ^ ^ ^ ^
  120 + | | | | | | | |
  121 + | | `--------------------+--------------------' | |
  122 + | | new state | | |
  123 + read | `------------------------+--------------------' |
  124 + write | new state name |
  125 + update V |
  126 + +-----------+ |
  127 + | data base |--------------------------------------------'
  128 + +-----------+ read only
  129 +
  130 +
  131 + When the client begins a session, there is no previous state. In this case, a default
  132 + 'initial state' is used instead.
  133 +
  134 + The data base may be updated by 'compute state' box, but should not be update by the
  135 + 'compute page' box. The 'compute page' box should be allowed only to read the data
  136 + base.
  137 +
  138 + In this file, all the above stuff is defined, except the 'compute state' and 'compute
  139 + page' boxes. You just have to provide the function for computing a new state (compute
  140 + state) and the function for computing the page (compute page) from the new state. You
  141 + don't have to worry about state names, saving and retrieving states and the like.
  142 +
  143 +
  144 +
  145 +
  146 +
  147 + ** (1.3) What web pages are made of.
  148 +
  149 + What the client can see in his browser's window may be called a 'page'. Within a page,
  150 + we have several sorts of components:
  151 +
  152 + - 'local' components, i.e. all components which do not open a connection, like
  153 + texts, images, etc... possibly using JavaScript programmation,
  154 +
  155 + - 'actioners', which, when clicked upon, open a connection with our web site; they
  156 + may appear as links or buttons, etc...
  157 +
  158 + - 'foreign links', which when clicked upon, open a connection with another web site
  159 + (or ours eventually).
  160 +
  161 + Of course, what an actioner does is just ask our web site to perform an action. To that
  162 + end, the actioner essentially sends the name of the action to be performed. However, it
  163 + may be necessary to provide additional informations which may be seen as 'operands' of
  164 + the action. In order to attach operands to an action, HTML provides the notion of
  165 + 'form'. Indeed, a form contains essentially a set of input fields into which the client
  166 + may put values for the required operands of the action, and a submit button, which is
  167 + the actioner itself. Notice that a single form may contain several submit buttons,
  168 + which simply means that there are several distincts actions taking the same set of
  169 + operands.
  170 +
  171 + Restrictions must be put on the use of all theses gadgets. Indeed, for example,
  172 + putting a form within another form is officially meaningless in HTML, and the client's
  173 + browser may be seriously disturbed by this. In this file, we propose an interface to
  174 + the HTML language, which forbids such meaningless things, simply by imposing a strict
  175 + typing of HTML concepts.
  176 +
  177 + Each web site may be accessible through two communication channels:
  178 +
  179 + - a non secured channel (HTTP),
  180 + - a secured channel (HTTPS).
  181 +
  182 + Nevertheless, the whole thing should be considered as a single web site. For example,
  183 + you may have a secured page, obtained through HTTPS, containing public images obtained
  184 + through HTTP. An actioner in a non secured page may open a secured connection, and
  185 + conversely.
  186 +
  187 + Summarizing, a web page is made of local elements, foreign links and actioners.
  188 + Actioners receive operands from forms, and they also choose to communicate through the
  189 + non secured or through the secured channel.
  190 +
  191 +
  192 +
  193 + +-------------------+
  194 + | page |
  195 + | | +---------------+
  196 + | +--------------+ | | next page |
  197 + | | form | | | (non secured) |
  198 + | | +----------+ | | HTTP | |
  199 + | | | actioner |---------------------------->| |
  200 + | | +----------+ | | +---------------+
  201 + | | | |
  202 + | | +----------+ | | +---------------+
  203 + | | | actioner |---------------------------->| next page |
  204 + | | +----------+ | | HTTPS | (secured) |
  205 + | | | | | |
  206 + | +--------------+ | | |
  207 + | | +---------------+
  208 + | |
  209 + +-------------------+
  210 +
  211 +
  212 + Notice that actioners need no be necessarily put into forms. In that case, they work as
  213 + ordinary links, but they still may receive operands as we shall see.
  214 +
  215 +
  216 +
  217 +
  218 + ** (1.4) Actions.
  219 +
  220 + The client opens a new connection with our web site whenever he clicks on an
  221 + actioner. The result is that a request is sent, essentially made of a list of 'web
  222 + arguments'. Each web argument is a pair (name,value). One of these web arguments, the
  223 + 'action' web argument (whose name is "a"), determines the action to be performed. The
  224 + other web arguments (not including "s", used to identify the state) are the operands
  225 + for this action.
  226 +
  227 + Hence, the 'compute state' box in the picture above, splits naturally into as many
  228 + sub-boxes as there are actions. For this reason, we define the following type for
  229 + representing actions (where '$State' is the type representing session informations):
  230 +
  231 +public type Web_Action($SessionTicket, $State):
  232 + http_action (String name, // name of action
  233 + (Maybe($State)) -> Bool allow, // true if action allowed
  234 + (HTTP_Info http_info,
  235 + List(Web_arg) web_args, // actually only 'operands' web arguments
  236 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
  237 + https_action (String name, // name of action
  238 + (Maybe($State)) -> Bool allow, // true if action allowed
  239 + (HTTP_Info http_info,
  240 + List(Web_arg) web_args, // actually only 'operands' web arguments
  241 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
  242 + http_https_action (String name,
  243 + (Maybe($State)) -> Bool allow, // true if action allowed
  244 + (HTTP_Info http_info,
  245 + List(Web_arg) web_args,
  246 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it).
  247 +
  248 + 'http_action's are executed only under HTTP, and 'https_action's are executed only
  249 + under HTTPS. 'http_https_action's may be executed under both types of connections.
  250 +
  251 + Each action has a name, which is used to identify the action. Each action also has a
  252 + function 'allow' whose job is to verify that the action is allowed in the current
  253 + state, and a function 'do_it' for performing the action. The function 'do_it' receives
  254 + a lot of informations:
  255 +
  256 + - 'HTTP informations':
  257 + - the IP address of the client,
  258 + - the URI requested by the client (after redirection),
  259 + - the list of HTTP headers generated by the client's browser,
  260 + - the list of web arguments sent by the client (except "s" and "a"),
  261 + - the previous state (or the 'initial' or 'ticket expired' state if no previous
  262 + state can be found).
  263 +
  264 + In most cases, HTTP informations are not used. This is the reason why they are gathered
  265 + for simplicity into a unique datum of type 'HTTP_Info'.
  266 +
  267 +// For your convenience, we introduce the following simpler variants:
  268 +//
  269 +//public define Web_Action($State)
  270 +// http_action
  271 +// (
  272 +// String name,
  273 +// $State -> Bool allow,
  274 +// (List(Web_arg),$State) -> $State do_it
  275 +// ) =
  276 +// http_action(name,
  277 +// (Maybe($State) ms) |-> if ms is
  278 +// {
  279 +// failure then true,
  280 +// success(s) then allow(s)
  281 +// },
  282 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  283 +// {
  284 +// failure then (failure, []),
  285 +// success(s2) then (success(do_it(l,s2)), [])
  286 +// }).
  287 +//
  288 +//public define Web_Action($State)
  289 +// https_action
  290 +// (
  291 +// String name,
  292 +// $State -> Bool allow,
  293 +// (List(Web_arg),$State) -> $State do_it
  294 +// ) =
  295 +// https_action(name,
  296 +// (Maybe($State) ms) |-> if ms is
  297 +// {
  298 +// failure then true,
  299 +// success(s) then allow(s)
  300 +// },
  301 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  302 +// {
  303 +// failure then (failure, []),
  304 +// success(s2) then (success(do_it(l,s2)), [])
  305 +// }).
  306 +//
  307 +//public define Web_Action($State)
  308 +// http_https_action
  309 +// (
  310 +// String name,
  311 +// $State -> Bool allow,
  312 +// (List(Web_arg),$State) -> $State do_it
  313 +// ) =
  314 +// http_https_action(name,
  315 +// (Maybe($State) ms) |-> if ms is
  316 +// {
  317 +// failure then true,
  318 +// success(s) then allow(s)
  319 +// },
  320 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  321 +// {
  322 +// failure then (failure, []),
  323 +// success(s2) then (success(do_it(l,s2)), [])
  324 +// }).
  325 +
  326 +
  327 +
  328 + When you define your web site, you must provide the list of all the actions of the
  329 + site. When a new state has been computed, a graphical representation of this state
  330 + must be sent to the client. To that end, you must provide a function (named below
  331 + 'compute_page') of type:
  332 +
  333 + $State -> HTML_Page
  334 +
  335 + where the type 'HTML_Page' (defined below in this file) abstractly represents HTML
  336 + pages.
  337 +
  338 +public type HTML_Page:...
  339 +
  340 + It should be clear that states and pages are deeply linked together. Indeed, we really
  341 + understand the page shown to the client as a representation of the current state of the
  342 + conversation between the client and the web site, but also containing informations
  343 + taken from the data bases.
  344 +
  345 +
  346 +
  347 +
  348 +
  349 + ** (1.5) States.
  350 +
  351 + Now, we explain how you can define the type (say 'State') to be used as an instance of
  352 + the type parameter '$State'. The following is just a suggestion.
  353 +
  354 + Each state determines a page (since 'compute_page' computes a page from a
  355 + state). However, some components of the state may be independant of the page. It may be
  356 + the case for example for the indication of the natural language used by the
  357 + client. Hence, a state should be made of (at least) two parts:
  358 +
  359 + - informations which are the same for all pages,
  360 + - informations which are particular to each page.
  361 +
  362 + For example, you could define:
  363 +
  364 + type Page: // one alternative per page, with particular informations
  365 + login(...), // in the components
  366 + main_page(...),
  367 + ...etc...
  368 +
  369 + Now, the type 'State' could be defined as follows:
  370 +
  371 + type State:
  372 + state(Language, // informations valid for all pages
  373 + ...,
  374 + Page). // informations particular to a page
  375 +
  376 + However, if you are making a secured web site within which clients should be identified
  377 + (by id and password), it may be a good idea to have two sorts of states, one for non
  378 + identified clients and one for identified clients. In this case, define the type
  379 + 'State' as follows (this is just a suggestion):
  380 +
  381 + type State:
  382 + non_identified(Language),
  383 + identified(String id,
  384 + Language,
  385 + Page).
  386 +
  387 + When a request arrives, check if the previous state is 'identified(...)' or
  388 + 'non_identified(...)', and don't provide access to certain pages to non identified
  389 + clients. This is required for security.
  390 +
  391 + Some more words on security. If your site needs to identify clients, define the
  392 + initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and
  393 + password of the client. If the id and password are correct, then change the state of
  394 + the client to 'identified(...)'. No other action should be able to do that. Now, be
  395 + confident that clients cannot forge states. The only information they have is the name
  396 + of a state, not the state itself which is never sent over the network, but only stored
  397 + on the server's disk. The name of the state is constructed using strong cryptographical
  398 + methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even
  399 + state names cannot be seen by a third party. So, if the system retrieves a previous
  400 + state of the form 'identified(...)', you can be confident that your client is well
  401 + identified, and you can send him confidential informations.
  402 +
  403 + States have a limited life time. It may happen that a client clicks on a button at a
  404 + time its state is out of date. In this case, this system considers that the new state
  405 + is a special state named 'ticket expired'. You must provide a function producing this
  406 + state when you describe your web site. The page corresponding to this state must just
  407 + inform the client that he/she waited a too long time before clicking on a button, and
  408 + has to restart (a new conversation) from the begining.
  409 +
  410 +
  411 +
  412 + * (2) Carrying on.
  413 +
  414 + ** (2.1) Describing your web sites.
  415 +
  416 + Before you may start your web site, you must describe it, i.e. produce a datum of the
  417 + opaque type 'Web_Site'.
  418 +
  419 +public type Web_Site:...
  420 +
  421 + Producing such a datum may be performed by:
  422 +
  423 +public define Web_Site
  424 + make_web_site_description
  425 + (
  426 + List(String) common_names, // for example: ["www.our-business.com",
  427 + // "192.168.0.1"]
  428 + // the second one is just for testing
  429 + String site_directory, // where 'public' and other directories are
  430 + // located (should NOT end with '/')
  431 + One -> One init,
  432 + (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
  433 + ($State expired,
  434 + HTTP_Info,
  435 + List(Web_arg),
  436 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
  437 + (HTTP_Info,
  438 + List(Web_arg),
  439 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
  440 + List(Web_Action($SessionTicket, $State)) actions,
  441 + (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
  442 + Int32 timeout, // seconds (todo: minutes)
  443 + List(Redirection) redirections,
  444 + String charset,
  445 + List(String) journal_extensions,
  446 + List(String) journal_headers,
  447 + String authorization_secret,
  448 + List(MIME) known_mime_types,
  449 + (String action_name,
  450 + List(Web_arg) args)-> One before_send_file
  451 + ).
  452 +
  453 +
  454 + Explanations:
  455 +
  456 + 'common_names' is the list of names of the site (the name the browser must send as the
  457 + value of the 'Host' HTTP header in order to access the site must be in that list). Such
  458 + a name generally looks like this:
  459 +
  460 + www.somewhere.com
  461 +
  462 + If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of
  463 + the site must be exactly the same as the name on the certificate (which is precisely
  464 + called the 'common name' in the X.509 jargon). If the two names do not match, the site
  465 + will still work, but the transaction will not be transparent to the client. His browser
  466 + will complain that the name of the certificate does not match the name of the site, and
  467 + he will have to accept the certificate manually.
  468 +
  469 + 'site_directory' is the absolute path to the directory where the files needed by the
  470 + site are located. Usually this directory looks like:
  471 +
  472 + my_anubis/web_sites/www.somewhere.com
  473 +
  474 + However, this information is not computed from 'common_name', so that you can change
  475 + the common name (for example temporarily, for networking reasons) without loosing
  476 + access to the files.
  477 +
  478 + 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state
  479 + whose graphical representation is a page explaining to the user that its 'ticket' (or
  480 + 'session information') has expired, and that he/she must close all popup windows and
  481 + start a new session. The arguments of the function contain the previous (expired)
  482 + state and all current informations concerning the user. This arguments may be useful
  483 + for example for producing the expiration message in the language chosen by the user.
  484 + You can also (and this may be much smarter) send a 'ticket prolongation page'
  485 + (including a new login for example), and resume the same conversation, since you have
  486 + all the pertinent informations at hand. In the case the ticket is definitely lost, the
  487 + second fonction 'ticket_lost_state' is used.
  488 +
  489 + Notice that despite the fact that the parameter $State is involved in the arguments of
  490 + the above function, the type 'Web_Site' does not depend on this parameter. This allows
  491 + to produce lists of web site descriptions, where each description may be constructed
  492 + with a different instance of $State. This is required because distinct sites must have
  493 + distinct types of session informations. This is made possible by the fact that the
  494 + type is obscure, and the constructor replaced by a function which assembles
  495 + 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single
  496 + entity not depending on $State. You should have a look to the private part of this file
  497 + if you want more precisions about this programming technique.
  498 +
  499 + 'charset' is a string which will determine the character encoding to be used by the
  500 + browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
  501 + etc...
  502 +
  503 + 'before_send_file' is a function which is executed just before the HTTP server sends a
  504 + file. It gets an action name and the web arguments received with the request for that
  505 + file. Notice that this action name and these web arguments may be put into a
  506 + 'private_download' element, and will come back to the server at the time of the
  507 + download.
  508 +
  509 +
  510 +
  511 + ** (2.2) Directories on the server's disk.
  512 +
  513 + The description of you site contains the name of the directory within which the
  514 + required files are located. This may be for example:
  515 +
  516 + my_anubis/web_sites/www.our-business.com/
  517 +
  518 + This is called the 'site directory' (for the given site). Within the site directory,
  519 + the following directories are created by this program:
  520 +
  521 + states
  522 + public
  523 + journal
  524 + private_download
  525 + upload_temporary
  526 +
  527 + The directory 'states' is used for storing states (session informations). Out of date
  528 + states are automatically removed after some time.
  529 +
  530 + The tree rooted at 'public' contains files that the server is allowed to send to the
  531 + clients. For security reasons, the server never sends a file which is not within the
  532 + tree whose root is this 'public' directory (except for the 'private download' mecanism;
  533 + see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')
  534 + must have been recognized before the file may be sent.
  535 +
  536 + The directory 'journal' contains the jounal files. The roles of the remaining
  537 + directories 'private_download' and 'upload_temporary' is explained in
  538 + 'multihost_http_server.anubis', where you will also find further informations on
  539 + 'public' and 'journal'.
  540 +
  541 +
  542 +
  543 +
  544 + ** (2.3) Web servers parameters.
  545 +
  546 + The web servers have several parameters useful for administration. They are described
  547 + as follows:
  548 +
  549 + public type WebServersParameters:
  550 + wsparms(Var(Bool) shutdown_required,
  551 +
  552 +
  553 +
  554 +
  555 + ** (2.4) Starting your web sites.
  556 +
  557 + When you have described all your web sites (you may want to have several web sites, and
  558 + they are distinguished by their 'common name'), you may start them all together using
  559 + 'start_web_sites' below. This function returns a result of the following type:
  560 +
  561 +public type Start_Web_Sites_Result:
  562 + cannot_bind_to_port(Int32),
  563 + cannot_bind_to_port(Int32,Int32),
  564 + ok(Server http_server,
  565 + Server https_server).
  566 +
  567 + Indeed, it may happen that the system cannot bind (begin to listen) to one of the two
  568 + ports (or to both). The main reason is that another server is already listening on that
  569 + port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that
  570 + the 's' bit has not been set for 'user' and 'group' (there is not such problem under
  571 + Windows). Also notice that the Linux kernel may need a rather long time (up to several
  572 + minutes) before liberating a listening port. Now, if the system can bind to the two
  573 + ports, the pair of the two servers is returned. Two tools are useful for manipulating
  574 + servers:
  575 +
  576 + shutdown of type Server -> One
  577 + is_down of type Server -> Bool
  578 +
  579 + They are defined in 'predefined.anubis' (together with the type 'Server').
  580 +
  581 +
  582 +public define Start_Web_Sites_Result
  583 + start_web_sites
  584 + (
  585 + Int32 ip_address, // the IP address shared by the web sites
  586 + Int32 http_port, // usually: 80
  587 + Int32 https_port, // usually: 443
  588 + String ssl_certificate_common_name,
  589 + List(Web_Site) web_sites, // web sites to be started
  590 + Var(Bool) shutdown_required
  591 + ).
  592 +
  593 + 'ip_address' is the IP address on which the two servers listen. If you put 0, the
  594 + servers listen on all the IP addresses of the machine. This may be useful if the
  595 + machine has several network interfaces.
  596 +
  597 + 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'
  598 + loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server
  599 + certificate. This is due to a problem of conception of SSL itself. See the book 'SSL
  600 + and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.
  601 +
  602 + Notice that the number of servers is always 2, regardless of the number of web sites
  603 + you are starting.
  604 +
  605 + The dynamic variable 'shutdown_required' may be used to control the shutdown of the two
  606 + servers from within the web site (typically the administration part). The servers will
  607 + shutdown as soon as this variable contains 'true'. So you must provide a variable
  608 + containing 'false' otherwise your servers will not run. You may also use the primitive
  609 + 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.
  610 +
  611 +
  612 +
  613 +
  614 +
  615 +
  616 + * (3) The HTML interface.
  617 +
  618 + We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination
  619 + of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more
  620 + reactive and attractive on the client side.
  621 +
  622 +
  623 + ** (3.1) Types used by the HTML interface.
  624 +
  625 + For easy reference, we gather below the definitions of all the types used by the HTML
  626 + interface, and we comment them immediately.
  627 +
  628 +
  629 +public type HTML_Size:
  630 + absolute(Int32), // in pixels
  631 + percentage(Int32).
  632 +
  633 +
  634 +public type Text_Option:
  635 + size(Int32), // size of character font to use
  636 + font(String), // name of character font to use (such as "helvetica",...)
  637 + color(RGB), // color to be used for characters
  638 + italic,
  639 + oblique,
  640 + small_capitals,
  641 + bold,
  642 + underlined,
  643 + left_justified,
  644 + right_justified,
  645 + justified, // justified on both sides
  646 + line_through,
  647 + nowrap,
  648 + class(String), //CSS class
  649 + id(String).
  650 +
  651 + A list of 'Text_Option' must be given with each text you want to put in your page.
  652 +
  653 + This indicate the way of reading text.
  654 +public type Reading_Way:
  655 + ltr, //the text is readable from "Left To Right" like english
  656 + rtl. //the text is readable from "Right To Left" like arabic
  657 +
  658 +
  659 +// Following types are define to prohib any arguments order error
  660 +
  661 +/**
  662 + * String value for 'id' attribut
  663 + */
  664 +public type HtmlId:
  665 + htmlId(String id).
  666 +
  667 +/**
  668 + * String value for 'class' attribut
  669 + */
  670 +public type HtmlClass:
  671 + htmlClass(String class).
  672 +
  673 +/**
  674 + * Name used in arguments list when form is submitted.
  675 + */
  676 +public type WebArgName:
  677 + wan(String name).
  678 +
  679 +/**
  680 + * Value passed to arguments list when form is submitted (for RadioButton and CheckBox).
  681 + */
  682 +public type WebArgValue:
  683 + wav(String value).
  684 +
  685 +/**
  686 + * Initial value of an input field.
  687 + */
  688 +public type InitialValue:
  689 + init(String value).
  690 +
  691 +public define Printable_tree [HtmlId x . Printable_tree y] = str_pt(x.id, y).
  692 +public define Printable_tree [HtmlClass x . Printable_tree y] = str_pt(x.class, y).
  693 +public define Printable_tree [WebArgName x . Printable_tree y] = str_pt(x.name, y).
  694 +public define Printable_tree [WebArgValue x . Printable_tree y] = str_pt(x.value, y).
  695 +public define Printable_tree [InitialValue x . Printable_tree y] = str_pt(x.value, y).
  696 +
  697 +
  698 +public type HtmlEvents:
  699 + // Form element events
  700 + onchange,
  701 + onsubmit,
  702 + onreset,
  703 + onselect,
  704 + onblur,
  705 + onfocus,
  706 + // Keyboard events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
  707 + onkeydown,
  708 + onkeypress,
  709 + onkeyup,
  710 + // Mouse events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
  711 + onclick,
  712 + ondblclick,
  713 + onmousedown,
  714 + onmousemove,
  715 + onmouseout,
  716 + onmouseover,
  717 + onmouseup.
  718 +
  719 +public type CoreAttrs:
  720 + id (String),
  721 + class (String),
  722 + style (String),
  723 + tooltip (String),
  724 + lang (String),
  725 + dir (Reading_Way),
  726 + accesskey (Word8),
  727 + tabindex (Int32),
  728 + attr (String, String),
  729 + event (HtmlEvents, String).
  730 +
  731 +public type InputAttrs:
  732 + class (String),
  733 + style (String),
  734 + tooltip (String),
  735 + lang (String),
  736 + dir (Reading_Way),
  737 + accesskey (Word8),
  738 + tabindex (Int32),
  739 + attr (String, String),
  740 + event (HtmlEvents, String).
  741 +
  742 +public type I18n:
  743 + lang (String),
  744 + dir (Reading_Way).
  745 +
  746 +//public type DIV_Option:
  747 +// id (String),
  748 +// class (String),
  749 +// style (String),
  750 +// tooltip (String),
  751 +// lang (String),
  752 +// dir (Reading_Way),
  753 +// attr (String, String),
  754 +// event (HtmlEvents, String).
  755 +//
  756 +
  757 + A list of 'DIV_Option' must be given with each DIV you want to put in your page.
  758 +
  759 +define String event_name
  760 + (
  761 + HtmlEvents e
  762 + ) =
  763 + if e is
  764 + {
  765 + // Form element events
  766 + onchange then "onchange",
  767 + onsubmit then "onsubmit",
  768 + onreset then "onreset",
  769 + onselect then "onselect",
  770 + onblur then "onblur",
  771 + onfocus then "onfocus",
  772 + // Keyboard events
  773 + onkeydown then "onkeydown",
  774 + onkeypress then "onkeypress",
  775 + onkeyup then "onkeyup",
  776 + // Mouse events
  777 + onclick then "onclick",
  778 + ondblclick then "ondblclick",
  779 + onmousedown then "onmousedown",
  780 + onmousemove then "onmousemove",
  781 + onmouseout then "onmouseout",
  782 + onmouseover then "onmouseover",
  783 + onmouseup then "onmouseup"
  784 + }.
  785 +
  786 +public type Table_Option:
  787 + background_color(RGB), // applied to all cells in the table
  788 + background_image(String url),
  789 + border(Int32 width_of_outer_edge, // if not present, all values are 0
  790 + Int32 width_of_top_of_relief,
  791 + Int32 width_of_inner_edge,
  792 + RGB border_color),
  793 + width(Int32), // sets a minimal width for the table
  794 + percentage_width(Int32).
  795 +
  796 +
  797 +public define Table_Option nude = border(0,0,0,rgb(0,0,0)).
  798 +
  799 +
  800 + A list of 'Table_Option' must be given with each table.
  801 +
  802 +
  803 +public type BackgroundOption:
  804 + repeat, // repeat the background in both directions
  805 + repeat_horizontal, // repeat the background only horizontally
  806 + repeat_vertical, // repeat the background only verticall
  807 + no_repeat, // don't repeat the background
  808 + center.
  809 +
  810 +
  811 +public type Cell_Option:
  812 + core_attrs(List(CoreAttrs)),
  813 + left, // put the content of the cell on the left
  814 + h_center, // center the content of the cell horizontally
  815 + right, // put the content of the cell on the right
  816 + top, // put the content of the cell upwards
  817 + v_center, // center the content of tye cell vertically,
  818 + bottom, // put the content of the cell downwards
  819 + base_line, // align the content vertically according to base lines
  820 + background_color(RGB),
  821 + background_image(String url, BackgroundOption),
  822 + width(Int32), // sets a minimal width for the cell
  823 + percentage_width(Int32),
  824 + height(Int32), // sets a minimal height for the cell
  825 + columns(Int32), // lets the cell span over several columns
  826 + rows(Int32), // lets the cell span over several rows
  827 + nowrap. // do not allow text wrapping within the cell
  828 +
  829 + A list of 'Cell_Option' must be given with each cell and each row in a table. Options
  830 + given with a row apply to all the cells in the row, but are superseded by options given
  831 + with cells, which apply only to the cell they are given with.
  832 +
  833 +
  834 +public type HTML_Cell($T):
  835 + cell(List(Cell_Option) options, $T content).
  836 +
  837 +public type HTML_Header_Cell($T):
  838 + header_cell(List(Cell_Option) options, $T content).
  839 +
  840 + The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',
  841 + depending on where you put your table (within a form or not within a form). For your
  842 + convenience, we define the following particular case:
  843 +
  844 +public define HTML_Cell($T)
  845 + cell
  846 + (
  847 + $T content
  848 + ) =
  849 + cell([],content).
  850 +
  851 +public define HTML_Header_Cell($T)
  852 + header_cell
  853 + (
  854 + $T content
  855 + ) =
  856 + header_cell([],content).
  857 +
  858 +
  859 +public type HTML_Row($T):
  860 + row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
  861 +
  862 +public type HTML_Header_Row($T):
  863 + empty,
  864 + header_row(List(Cell_Option) options, List(HTML_Header_Cell($T)) cells).
  865 +
  866 + Same remark as for 'HTML_Cell($T)'. We define several convenience functions:
  867 +
  868 +public define HTML_Row($T)
  869 + row
  870 + (
  871 + List(HTML_Cell($T)) cells
  872 + ) =
  873 + row([],cells).
  874 +
  875 +public define HTML_Row($T)
  876 + row
  877 + (
  878 + HTML_Cell($T) cell
  879 + ) =
  880 + row([],[cell]).
  881 +
  882 +public define HTML_Header_Row($T)
  883 + header_row
  884 + (
  885 + List(HTML_Header_Cell($T)) cells
  886 + ) =
  887 + header_row([],cells).
  888 +
  889 +public define HTML_Header_Row($T)
  890 + header_row
  891 + (
  892 + HTML_Header_Cell($T) cell
  893 + ) =
  894 + header_row([],[cell]).
  895 +
  896 +public type Actioner_Connection:
  897 + same, // use same type of connection as current page
  898 + http, // use non secured connection
  899 + https. // use secured connection
  900 +
  901 +public type Other_Window_Option:
  902 + resizable, // the new window may be resized by the client
  903 + scrollbars, // the new window has scrollbars
  904 + width(Int32), // the new window has the specified width
  905 + height(Int32). // the new window has the specified height
  906 +
  907 +public type Actioner_Target:
  908 + same,
  909 + same (String label),
  910 + other(String window_name, List(Other_Window_Option)).
  911 +
  912 +public type Actioner_Aspect:
  913 + link (List(Text_Option),String text), // hypertext link
  914 + push_button (List(CoreAttrs),String text),
  915 + submit (List(CoreAttrs),String text),
  916 + button (String url_off, String url_on), // rollover button
  917 + button (String url_off, String url_on, Int32 w, Int32 h), // idem with size
  918 + immediate_selector (WebArgName name, Int32 size, List((WebArgValue,String)) choices),
  919 + immediate_selector (WebArgName name, Int32 size, List((WebArgValue,String)) choices, InitialValue selected).
  920 +
  921 +
  922 +
  923 +public type Actioner_Local_Action:
  924 + close_window.
  925 +
  926 +
  927 +public define Actioner_Aspect
  928 + link
  929 + (
  930 + String text
  931 + ) =
  932 + link([],text).
  933 +
  934 +
  935 +public define Actioner_Aspect
  936 + link
  937 + (
  938 + List(Text_Option) options,
  939 + Int32 i
  940 + ) =
  941 + link(options,integer_to_string(i)).
  942 +
  943 +public define Actioner_Aspect
  944 + link
  945 + (
  946 + Int32 i
  947 + ) =
  948 + link([],i).
  949 +
  950 +public define Actioner_Aspect
  951 + button
  952 + (
  953 + String url_img
  954 + ) =
  955 + button(url_img,url_img).
  956 +
  957 +
  958 +
  959 + Actioners are explained in details below.
  960 +
  961 +
  962 +public type TextAreaOption:
  963 + disabled,
  964 + read_only,
  965 + wrap_lines.
  966 +
  967 +public type HTML_In_Form:
  968 + literal_pt (Printable_tree),
  969 + literal (String),
  970 + sequence (List(HTML_In_Form) items),
  971 + text (List(Text_Option), String the_text),
  972 + preformated (List(Text_Option), String),
  973 + paragraph (List(Text_Option), HTML_In_Form content),
  974 + image (String url),
  975 + image (String url, Int32 width, Int32 height),
  976 + table (List(Table_Option), HTML_Header_Row(HTML_In_Form), List(HTML_Row(HTML_In_Form))),
  977 + center (HTML_In_Form),
  978 + mail_to (String email, HTML_In_Form element),
  979 + scroller (Int32 width, Int32 height,
  980 + Int32 content_width, Int32 content_height,
  981 + HTML_In_Form content),
  982 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  983 + String action_name, List((String,String)) extra_ops,
  984 + List(Actioner_Local_Action)),
  985 + foreign_link (List(Text_Option), String url),
  986 + foreign_link (List(Text_Option), String url, String name),
  987 + private_download (String abs_path, String name, String extra_ext,
  988 + Maybe((String,List((String,String)))) action),
  989 + text_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),
  990 + text_input_ro (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),
  991 + password_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),
  992 + text_area (List(TextAreaOption), WebArgName name, InitialValue init, Int32 width, Int32 height),
  993 + file_upload (String label, HtmlId id, WebArgName name, Int32 width),
  994 + selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List(String) choices),
  995 + selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List(String) choices, InitialValue selected),
  996 + // List((String,String)) = List((code,name)) where :
  997 + // code is the web-arg value
  998 + // name appears in selector
  999 + selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List((WebArgValue,String)) choices),
  1000 + selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List((WebArgValue,String)) choices, InitialValue selected),
  1001 + radio_button (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1002 + radio_button_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1003 + check_box (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1004 + check_box_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
  1005 + div (List(CoreAttrs), HTML_In_Form content),
  1006 + div_empty (List(CoreAttrs)),
  1007 + hidden (WebArgName name, InitialValue init).
  1008 +
  1009 +
  1010 + 'HTML_In_Form' defines all the elements you may put within a form. We define a
  1011 + convenience function:
  1012 +
  1013 +public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).
  1014 +
  1015 +public define HTML_In_Form
  1016 + foreign_link
  1017 + (
  1018 + Int32 tsize,
  1019 + String url,
  1020 + String name
  1021 + ) =
  1022 + foreign_link([size(tsize)],url,name).
  1023 +
  1024 +public define HTML_In_Form
  1025 + actioner
  1026 + (
  1027 + Actioner_Connection conn,
  1028 + Actioner_Target targ,
  1029 + Actioner_Aspect asp,
  1030 + String action_name,
  1031 + List((String,String)) extra_ops
  1032 + ) =
  1033 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  1034 +
  1035 +public define HTML_In_Form
  1036 + actioner
  1037 + (
  1038 + Actioner_Connection conn,
  1039 + Actioner_Target targ,
  1040 + Actioner_Aspect asp,
  1041 + ) =
  1042 + actioner(conn, targ, asp, "", [], []).
  1043 +
  1044 +
  1045 +public define HTML_In_Form
  1046 + text_area
  1047 + (
  1048 + WebArgName name,
  1049 + InitialValue init,
  1050 + Int32 width,
  1051 + Int32 height
  1052 + ) =
  1053 + text_area([],name,init,width,height).
  1054 +
  1055 +public define HTML_In_Form
  1056 + table
  1057 + (
  1058 + List(HTML_Row(HTML_In_Form)) rows
  1059 + ) =
  1060 + table([],empty,rows).
  1061 +
  1062 +public define HTML_In_Form
  1063 + table
  1064 + (
  1065 + List(Table_Option) options,
  1066 + List(HTML_Row(HTML_In_Form)) rows
  1067 + ) =
  1068 + table(options,empty,rows).
  1069 +
  1070 +public define HTML_In_Form
  1071 + table
  1072 + (
  1073 + HTML_Header_Row(HTML_In_Form) h_row,
  1074 + List(HTML_Row(HTML_In_Form)) rows
  1075 + ) =
  1076 + table([],h_row,rows).
  1077 +
  1078 +public define HTML_In_Form
  1079 + private_download
  1080 + (
  1081 + String abs_path,
  1082 + String name,
  1083 + String extra_ext
  1084 + ) =
  1085 + private_download(abs_path,name,extra_ext,failure).
  1086 +
  1087 +public define HTML_In_Form
  1088 + private_download
  1089 + (
  1090 + String abs_path,
  1091 + String name,
  1092 + String extra_ext,
  1093 + String action_name,
  1094 + List((String,String)) args
  1095 + ) =
  1096 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  1097 +
  1098 +public define HTML_In_Form
  1099 + text
  1100 + (
  1101 + String s
  1102 + ) =
  1103 + text([],s).
  1104 +
  1105 +public define HTML_In_Form
  1106 + radio_button (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1107 + = radio_button (options, label_text, htmlId(n.name), n, value, checked).
  1108 +
  1109 +public define HTML_In_Form
  1110 + radio_button_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1111 + = radio_button_r (options, label_text, htmlId(n.name), n, value, checked).
  1112 +
  1113 +public define HTML_In_Form
  1114 + check_box (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1115 + = check_box (options, label_text, htmlId(n.name), n, value, checked).
  1116 +
  1117 +public define HTML_In_Form
  1118 + check_box_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
  1119 + = check_box_r (options, label_text, htmlId(n.name), n, value, checked).
  1120 +
  1121 +public define HTML_In_Form
  1122 + file_upload (WebArgName name, Int32 width)
  1123 + = file_upload("", htmlId(""), name, width).
  1124 +
  1125 +
  1126 +public type HTML_Off_Form:
  1127 + literal_pt (Printable_tree),
  1128 + literal (String),
  1129 + sequence (List(HTML_Off_Form) items),
  1130 + text (List(Text_Option), String the_text),
  1131 + preformated (List(Text_Option), String),
  1132 + paragraph (List(Text_Option), HTML_Off_Form content),
  1133 + image (String url),
  1134 + image (String url, Int32 width, Int32 height),
  1135 + table (List(Table_Option), HTML_Header_Row(HTML_Off_Form), List(HTML_Row(HTML_Off_Form))),
  1136 + center (HTML_Off_Form),
  1137 + mail_to (String email, HTML_Off_Form element),
  1138 + scroller (Int32 width, Int32 height,
  1139 + Int32 content_width, Int32 content_height,
  1140 + HTML_Off_Form content),
  1141 + fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),
  1142 + fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  1143 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  1144 + String action_name, List((String,String)) extra_ops,
  1145 + List(Actioner_Local_Action)),
  1146 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  1147 + String action_name, List((String,String)) extra_ops,
  1148 + List(Actioner_Local_Action), String form_name),
  1149 + foreign_link (List(Text_Option), String url),
  1150 + foreign_link (List(Text_Option), String url, String name),
  1151 + private_download (String abs_path, String name, String extra_ext,
  1152 + Maybe((String,List((String,String)))) action),
  1153 + label (String name),
  1154 + form (String form_name, List(CoreAttrs), HTML_In_Form content),
  1155 + form (String form_name, List(CoreAttrs),
  1156 + String action_name, List((String,String)) extra_ops,
  1157 + HTML_In_Form content),
  1158 + div (List(CoreAttrs), HTML_Off_Form content),
  1159 + div_empty (List(CoreAttrs)).
  1160 +
  1161 + 'HTML_Off_Form' defines all the elements you may put outside any form.
  1162 +
  1163 +
  1164 +public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).
  1165 +public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)
  1166 + = fixed_size_2(width,height,name_of_HTML_file).
  1167 +
  1168 +
  1169 +public define HTML_Off_Form
  1170 + foreign_link
  1171 + (
  1172 + Int32 tsize,
  1173 + String url,
  1174 + String name
  1175 + ) =
  1176 + foreign_link([size(tsize)],url,name).
  1177 +
  1178 +
  1179 +public define HTML_Off_Form
  1180 + actioner
  1181 + (
  1182 + Actioner_Connection conn,
  1183 + Actioner_Target targ,
  1184 + Actioner_Aspect asp,
  1185 + String action_name,
  1186 + List((String,String)) extra_ops
  1187 + ) =
  1188 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  1189 +
  1190 +public define HTML_Off_Form
  1191 + table
  1192 + (
  1193 + List(HTML_Row(HTML_Off_Form)) rows
  1194 + ) =
  1195 + table([],empty,rows).
  1196 +
  1197 +public define HTML_Off_Form
  1198 + table
  1199 + (
  1200 + List(Table_Option) options,
  1201 + List(HTML_Row(HTML_Off_Form)) rows
  1202 + ) =
  1203 + table(options,empty,rows).
  1204 +
  1205 +public define HTML_Off_Form
  1206 + table
  1207 + (
  1208 + HTML_Header_Row(HTML_Off_Form) h_row,
  1209 + List(HTML_Row(HTML_Off_Form)) rows
  1210 + ) =
  1211 + table([],h_row,rows).
  1212 +
  1213 + We add two convenience functions for 'row'. The reason why we add two functions, one
  1214 + for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an
  1215 + arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,
  1216 + the arguments of the function do not refer to any of the types defined here.
  1217 +
  1218 +public define HTML_Row(HTML_In_Form)
  1219 + row
  1220 + (
  1221 + HTML_In_Form content
  1222 + ) =
  1223 + row([],[cell([],content)]).
  1224 +
  1225 +public define HTML_Row(HTML_Off_Form)
  1226 + row
  1227 + (
  1228 + HTML_Off_Form content
  1229 + ) =
  1230 + row([],[cell([],content)]).
  1231 +
  1232 +
  1233 +public define HTML_Off_Form
  1234 + private_download
  1235 + (
  1236 + String abs_path,
  1237 + String name,
  1238 + String extra_ext
  1239 + ) =
  1240 + private_download(abs_path,name,extra_ext,failure).
  1241 +
  1242 +
  1243 +public define HTML_Off_Form
  1244 + private_download
  1245 + (
  1246 + String abs_path,
  1247 + String name,
  1248 + String extra_ext,
  1249 + String action_name,
  1250 + List((String,String)) args
  1251 + ) =
  1252 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  1253 +
  1254 +public define HTML_Off_Form
  1255 + text
  1256 + (
  1257 + List(Text_Option) lto,
  1258 + Int32 i
  1259 + ) =
  1260 + text(lto,integer_to_string(i)).
  1261 +
  1262 +
  1263 +public define HTML_Off_Form
  1264 + text
  1265 + (
  1266 + Int32 i
  1267 + ) =
  1268 + text([],i).
  1269 +
  1270 +public define HTML_Off_Form
  1271 + text
  1272 + (
  1273 + String s
  1274 + ) =
  1275 + text([],s).
  1276 +
  1277 + - Cell a gap between two other cells :
  1278 +
  1279 +public define HTML_Cell(HTML_Off_Form)
  1280 + width_gap
  1281 + (
  1282 + Int32 w
  1283 + ) =
  1284 + cell([width(w)],text([],"")).
  1285 +
  1286 +public define HTML_Cell(HTML_In_Form)
  1287 + width_gap
  1288 + (
  1289 + Int32 w
  1290 + ) =
  1291 + cell([width(w)],text([],"")).
  1292 +
  1293 +
  1294 + - Row a gap between two other rows :
  1295 +
  1296 +public define HTML_Row(HTML_Off_Form)
  1297 + height_gap
  1298 + (
  1299 + Int32 h
  1300 + ) =
  1301 + row([],[cell([height(h)],text([],""))]).
  1302 +
  1303 +public define HTML_Row(HTML_In_Form)
  1304 + height_gap
  1305 + (
  1306 + Int32 h
  1307 + ) =
  1308 + row([],[cell([height(h)],text([],""))]).
  1309 +
  1310 +
  1311 + Notice that the two types have alternatives in common (same name, same arguments types,
  1312 + up to the value of the parameter $T), which correspond to elements which may be put
  1313 + anywhere in the page.
  1314 +
  1315 +
  1316 +
  1317 +public type CSS_Style:
  1318 + text_options(List(Text_Option)).
  1319 +
  1320 +public type CSS_File:
  1321 + css_file(String file_name).
  1322 +
  1323 +public type JS_File:
  1324 + js_file(String file_name).
  1325 +
  1326 +define String
  1327 + format
  1328 + (
  1329 + List(Text_Option) l
  1330 + ).
  1331 +
  1332 +
  1333 +
  1334 +
  1335 +define Printable_tree
  1336 + format_css_styles
  1337 + (
  1338 + List(CSS_Style) l
  1339 + ) =
  1340 + if l is
  1341 + {
  1342 + [ ] then [ ],
  1343 + [h . t] then
  1344 + [ if h is
  1345 + {
  1346 + text_options(tos) then
  1347 + [" body, span, p { ", format(tos), " }\n" ]
  1348 + }
  1349 + . format_css_styles(t)]
  1350 + }.
  1351 +
  1352 +
  1353 +
  1354 +public type HTML_Meta:
  1355 + keywords (List(String)),
  1356 + refresh (Actioner_Connection connection,
  1357 + Actioner_Target target,
  1358 + String action_name,
  1359 + Int32 delay), // in seconds
  1360 + meta (String name, String content),
  1361 + http_equiv (String name, String content),
  1362 + generic_meta (List((String,String))),
  1363 + literal (String).
  1364 +
  1365 + Meta tags are put in the 'head' of the HTML page.
  1366 +
  1367 +
  1368 +public type Body_Option:
  1369 + background_color (RGB),
  1370 + background_image (String url),
  1371 + background_image (String url, List(BackgroundOption)).
  1372 +
  1373 +public type HTML_Body:
  1374 + body(List(Body_Option) options, HTML_Off_Form content).
  1375 +
  1376 +
  1377 +public type HTML_Page:
  1378 + html_page(String title,
  1379 + List(HTML_Meta) meta_tags,
  1380 + List(CSS_Style) styles,
  1381 + List(CSS_File) css_files,
  1382 + List(JS_File) js_files,
  1383 + HTML_Body body).
  1384 +
  1385 +public define HTML_Page
  1386 + html_page
  1387 + (
  1388 + String title,
  1389 + List(HTML_Meta) metas,
  1390 + HTML_Body body
  1391 + ) =
  1392 + html_page(title,metas,[],[],[],body).
  1393 +
  1394 +public define HTML_Page
  1395 + html_page
  1396 + (
  1397 + String title,
  1398 + List(HTML_Meta) metas,
  1399 + List(CSS_Style) styles,
  1400 + HTML_Body body
  1401 + ) =
  1402 + html_page(title, metas, styles, [], [], body).
  1403 +
  1404 + 'HTML_Page' represents the final product of the construction of a web page.
  1405 +
  1406 +
  1407 +
  1408 +
  1409 + *** (3.2) ``in form'' versus ``off form''.
  1410 +
  1411 + There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some
  1412 + of them may have a content, which is yet another HTML element (or several). Hence, it
  1413 + is meaningful to say that an element is 'within' another one. Now, putting any element
  1414 + within any other one may be meaningless. For example, an input element must be put
  1415 + within a form (otherwise, it is useless), and a from within another form has no precise
  1416 + meaning (and is forbidden by the HTML specification).
  1417 +
  1418 + Actually, the main criterium is ``within a form or not within a form''. So, HTML
  1419 + elements in a given page are separated into two categories: those who are within a
  1420 + form, and the others. Nevertheless, there are elements which may belong to both
  1421 + categories, like images and texts. We want to make use of the strong typing mecanism of
  1422 + Anubis in order to forbid non meaningful placement of elements.
  1423 +
  1424 + The type 'HTML_In_Form' defines elements to be put within forms. Similarly,
  1425 + 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,
  1426 + and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),
  1427 + but the two types are not cross recursive. This is the reason why it is impossible to
  1428 + put a form within a form. In order to construct a web page, you essentially have to
  1429 + produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').
  1430 +
  1431 + In practice, you don't have to worry so much about these two types, because elements
  1432 + which may be put anywhere are constructed for both types by functions with the same
  1433 + name and the same arguments. Hence, for both types, you just write the same thing. You
  1434 + are warned by the compiler only when you try to put an element at a place it is not
  1435 + allowed.
  1436 +
  1437 +
  1438 +
  1439 + *** (3.3) Defining your own style.
  1440 +
  1441 + We provide generic tools for constructing HTML elements. However, your web site needs
  1442 + to have a ``style''.
  1443 +
  1444 + To that end, you need to write down a set of ``styling functions'', using the tools
  1445 + defined here. These styling functions allow the introduction of your colors and other
  1446 + visual characteristics into the constructed elements once and for all. For example, you
  1447 + may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using
  1448 + some 'text_color'. You may write something like this:
  1449 +
  1450 + define RGB text_color = rgb(10,40,40).
  1451 +
  1452 + define HTML_Off_Form
  1453 + text
  1454 + (
  1455 + String the_text
  1456 + ) =
  1457 + text([font("helvetica"),size(14),color(text_color)],
  1458 + the_text).
  1459 +
  1460 + (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a
  1461 + page, you just write:
  1462 +
  1463 + text("... some text ...")
  1464 +
  1465 + and you don't have to provide the font, size and color for each text. If you want to
  1466 + have several styles of text presentation, you just write several sets of such
  1467 + convenience functions. This also suggests a trick. You may want for example different
  1468 + colors for 'in form' texts and 'off form' texts. This may be achieved automatically by
  1469 + defining two functions as above, with the same name and same argument type, but
  1470 + returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.
  1471 +
  1472 + If this preliminary work is well done, you will not waste your time later when you
  1473 + concentrate on the actual informational content of your pages.
  1474 +
  1475 + This general principle should be applied to all sorts of elements. This is the best
  1476 + thing to do in order to separate the functions defining the visual style from the
  1477 + functions defining the informational content itself, so that changing the style without
  1478 + changing the content becomes easy. This is also the best way for having a clean and
  1479 + easily readable source for your web site.
  1480 +
  1481 +
  1482 +
  1483 +
  1484 +
  1485 + *** (3.4) Actioners and forms.
  1486 +
  1487 + We have gathered several notions from HTML into that of an 'actioner'. An actioner is
  1488 + an HTML element which opens a connection to our server when clicked upon. Actioners may
  1489 + have different visual aspects. They may look like hypertext links or like buttons
  1490 + (rollovers), or even like selectors (with immediate action). In any case, their
  1491 + behavior is the same: they open a connection to our server, and send a set of 'web
  1492 + arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes
  1493 + the action to be performed, and the others should be considered as operands for this
  1494 + action. Actually, the precise behavior of the actioner has several variants.
  1495 +
  1496 + The connection with the server may be secured (HTTPS) or non secured (HTTP). See the
  1497 + type 'Actioner_Connection' above.
  1498 +
  1499 + You must also choose where the answer must be rendered. This may be in the same window
  1500 + or in another window (or frame). If it is in another window, the name of that window
  1501 + must be given. If the window does not exist, the browser will create it. Optionally,
  1502 + you may give the dimensions of the new window and other characteristics. See the type
  1503 + 'Actioner_Target' above.
  1504 +
  1505 + The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the
  1506 + case of a rollover button, you provide the URLs of two images (of the same size)
  1507 + representing the button:
  1508 +
  1509 + url_off: to be used when the mouse is not over the button,
  1510 + url_on: to be used when the mouse is over the button.
  1511 +
  1512 + You can also create rollover buttons without creating images. Just use the second
  1513 + alternative named 'button'. The server creates the images automatically.
  1514 +
  1515 + The purpose of forms is just to give operands to actioners. If the actioner is placed
  1516 + within a form, all the input elements which are within this form provide operands to
  1517 + the actioner (except sometimes when they are not set by the client). If it is not put
  1518 + within a form, the actioner gets no operand, except if the name of a form is explicitly
  1519 + given, in which case the actioner gets all the inputs from that form as
  1520 + operands. Furthermore, you may want to give extra operands to the actioner. This may be
  1521 + useful for separating families of actioners with the same action name. Extra operands
  1522 + 'name=value' must be given in the form of pairs '(name,value)'.
  1523 +
  1524 + Notice that the name of a form may be used by an actioner which is off the form, so as
  1525 + to get the operands provided by this form. Also notice that several actioners may refer
  1526 + to the same form, being either in the form, or referring to the form from the
  1527 + outside. These actioners simply get the same set of operands, even if they correspond
  1528 + to distinct actions.
  1529 +
  1530 + Input elements may be put only within a form.
  1531 +
  1532 +
  1533 +
  1534 + *** (3.5) Local popup.
  1535 +
  1536 + This element looks like a link or a rollover button. When this button is clicked upon,
  1537 + a 'popup window' appears. Actually, this popup window is just a layer in the same HTML
  1538 + page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In
  1539 + particular, clicking on the button does not open any connection. This is why it is
  1540 + called 'local'. The arguments have the following roles:
  1541 +
  1542 + Actioner_Aspect aspect of the button (same semantics as for actioners)
  1543 + content content of the popup window
  1544 + x, y, position of the popup window on the HTML page (not relative
  1545 + to the button but to the page itself)
  1546 + title title of the popup window
  1547 + color color of the title bar and close button in the popup window.
  1548 + A lightened version of this color is used for the background
  1549 + of the popup window.
  1550 + width width of the title bar
  1551 +
  1552 +
  1553 +
  1554 +
  1555 +
  1556 + --- That's all for the public part ! --------------------------------------------------
  1557 +
  1558 +
  1559 +
  1560 +
  1561 +
  1562 + ----------------------------------- Table of Contents ---------------------------------
  1563 +
  1564 + *** [1] States.
  1565 + *** [1.1] Saving and retrieving states.
  1566 + *** [1.2] Deleting out of date states.
  1567 +
  1568 + *** [2] Tools.
  1569 + *** [2.1] Directories.
  1570 + *** [2.2] Secondary documents.
  1571 +
  1572 + *** [3] Managing web arguments.
  1573 + *** [3.1] Prefixing web arguments names.
  1574 + *** [3.2] Separating web arguments.
  1575 + *** [3.3] Applying an action.
  1576 +
  1577 + *** [4] Web site descriptions and the 'awp handlers'.
  1578 + *** [4.1] The type 'Web_Site'.
  1579 + *** [4.2] Making a web site description.
  1580 + *** [4.3] Starting the servers.
  1581 +
  1582 + *** [5] HTML Formating.
  1583 + *** [5.1] The type 'HTML_Any($T)'.
  1584 + *** [5.2] Formating a color.
  1585 + *** [5.3] Creating buttons.
  1586 + *** [5.4] Formating an actioner.
  1587 + *** [5.5] Formating a private download link.
  1588 + *** [5.6] Formating rows and cells in a table.
  1589 + *** [5.7] Formating elements which may be put anywhere.
  1590 + *** [5.8] Formating 'in form' elements.
  1591 + *** [5.9] Formating 'off form' elements.
  1592 + *** [5.10] Formating meta-tags.
  1593 +
  1594 + ---------------------------------------------------------------------------------------
  1595 +
  1596 +
  1597 +
  1598 +
  1599 +public define String
  1600 + doctype_w3c_header
  1601 + =
  1602 + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "+
  1603 + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".
  1604 +
  1605 +
  1606 +
  1607 +
  1608 + *** [1] States.
  1609 +
  1610 + We have to define functions for saving a state, retrieving a state, deleting out of
  1611 + date states. We need one such function per web site. The types of the first two
  1612 + functions depend on the parameter $State. This is not the case of the third one. The
  1613 + fact that the instance of $State is variable from one web sites to the other implies
  1614 + rather subtle manipulations using full functionality.
  1615 +
  1616 +
  1617 + *** [1.1] Saving and retrieving states.
  1618 +
  1619 + Each state is saved into a file on the server's disk (in the directory represented by
  1620 + the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The
  1621 + state is saved together with a time stamp whose value is obtained by adding the current
  1622 + time to the given timeout for states. The state receives a name obtained by hashing
  1623 + (using sha1) the content of the file itself, and then encoding the hash with
  1624 + 'web_arg_encode'. The name of the file into which the state is saved is the
  1625 + concatenation of "s" and the name of the state.
  1626 +
  1627 +read CXM_web_arg_encode.anubis
  1628 +
  1629 + The tool below constructs the function which is able to save a state on the server's
  1630 + disk.
  1631 +
  1632 +define (Maybe($State) s) -> String // the function constructed returns the name of the state
  1633 + make_save_state_function
  1634 + (
  1635 + Int32 timeout,
  1636 + String state_directory
  1637 + ) =
  1638 + (Maybe($State) mbs) |->
  1639 + if mbs is
  1640 + {
  1641 + failure then "",
  1642 + success(s) then
  1643 + with time_stamp = now+timeout,
  1644 + to_be_saved = (time_stamp,s),
  1645 + state_name = web_arg_encode(sha1(s)),
  1646 + if save(to_be_saved,state_directory+"/s"+state_name) is ok
  1647 + then state_name
  1648 + else (print("Cannot create state file in '"+state_directory+"'.\n"); "")
  1649 + }.
  1650 +
  1651 +
  1652 + When a request arrives, we need to retrieve the previous state from the server's
  1653 + disk. We receive the name of that state. If the state is out of date, the state file is
  1654 + kept 3 days, and then deleted.
  1655 +
  1656 +type PreviousState($State):
  1657 + not_found, // cannot retrieve the previous state
  1658 + out_of_date($State), // the previous state is out of date
  1659 + still_valid($State). // the previous state is still valid
  1660 +
  1661 +define (String state_name) -> PreviousState($State)
  1662 + make_retrieve_state_function
  1663 + (
  1664 + String state_directory
  1665 + ) =
  1666 + (String state_name) |->
  1667 + with file_path = state_directory+"/s"+state_name,
  1668 + if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
  1669 + then (
  1670 + if d is (time_stamp,s) then
  1671 + if time_stamp < now
  1672 + then (
  1673 + forget(remove(file_path));
  1674 + out_of_date(s)
  1675 + )
  1676 + else still_valid(s) // state has been successfully retrieved
  1677 + )
  1678 + else not_found.
  1679 +
  1680 +
  1681 +
  1682 + *** [1.2] Deleting out of date states.
  1683 +
  1684 + We also need to delete states which are out of date and which will never be deleted by
  1685 + the above method. This may be performed by a machine doing this periodically (say once
  1686 + per states life time period).
  1687 +
  1688 +define (List(String) file_names) -> One
  1689 + make_delete_out_of_date_states_function
  1690 + (
  1691 + Maybe($State) dummy,
  1692 + String state_directory
  1693 + ) =
  1694 + (List(String) file_names) |-df->
  1695 + if file_names is
  1696 + {
  1697 + [ ] then unique,
  1698 + [h . t] then
  1699 + with file_path = state_directory+"/"+h,
  1700 + if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
  1701 + then (
  1702 + if d is (time_stamp,data) then
  1703 + if time_stamp < now
  1704 + then (forget(remove(file_path)); df(t))
  1705 + else df(t)
  1706 + )
  1707 + else (forget(remove(file_path)); df(t))
  1708 + }.
  1709 +
  1710 +
  1711 + The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.
  1712 +
  1713 + Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the
  1714 + function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler
  1715 + does not accept a parameter in the body of a function (here the parameter is required
  1716 + by the use of 'retrieve') if this parameter does not appear in the type of the
  1717 + function. This is because this would create ambiguities that no explicit typing may
  1718 + ever resolve. If you put a double slash in front of the declaration of 'dummy' above,
  1719 + and if you compile this file, you will get a message like this one:
  1720 +
  1721 + Error in 'making_a_web_site.anubis', line 1300, column 7:
  1722 + A definition may not contain parameters which are not present
  1723 + in the declaration part (hidden parameters):
  1724 + $State
  1725 +
  1726 + The type of the function constructed by 'make_delete_out_of_date_states_function' is
  1727 + independant of the parameter $State. This is important because this allows to create
  1728 + the list of such functions for all web sites. From this list, it is possible to call
  1729 + the functions one after the other, so deleting out of date states for all web
  1730 + sites. Actually, the next function receives a list of pairs (state_directory,function),
  1731 + one for each web site.
  1732 +
  1733 +define One
  1734 + delete_out_of_date_states // for all web sites
  1735 + (
  1736 + List((String, List(String) -> One)) directories_and_functions
  1737 + ) =
  1738 + if directories_and_functions is
  1739 + {
  1740 + [ ] then unique,
  1741 + [h . t] then if h is (state_directory,function) then
  1742 + function(directory_list(state_directory,"s*"));
  1743 + delete_out_of_date_states(t)
  1744 + }.
  1745 +
  1746 +
  1747 + The above function must be called periodically in a separate virtual machine. The
  1748 + period we have choosen is (rather logically) the life time of states itself. This may
  1749 + be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not
  1750 + be really infinite, because the servers may be shutdown. Hence, our loop must test
  1751 + (rather frequently; say every second) if the servers are down. If they are, the loop
  1752 + must be exited.
  1753 +
  1754 +define One
  1755 + delete_states_loop
  1756 + (
  1757 + List((String,List(String) -> One)) directories_and_functions,
  1758 + Int32 timeout,
  1759 + Int32 next_time,
  1760 + Server http_server,
  1761 + Server https_server,
  1762 + Var(Bool) shutdown_required
  1763 + ) =
  1764 + if *shutdown_required
  1765 + then (shutdown(http_server); shutdown(https_server))
  1766 + else unique;
  1767 + if (is_down(http_server) & is_down(https_server))
  1768 + then unique
  1769 + else if now > next_time
  1770 + then
  1771 + (
  1772 + delete_out_of_date_states(directories_and_functions);
  1773 + delete_states_loop(directories_and_functions,
  1774 + timeout,
  1775 + now+timeout,
  1776 + http_server,
  1777 + https_server,
  1778 + shutdown_required)
  1779 + )
  1780 + else
  1781 + (
  1782 + sleep(1000); // sleep just one second and try again
  1783 + delete_states_loop(directories_and_functions,
  1784 + timeout,
  1785 + next_time,
  1786 + http_server,
  1787 + https_server,
  1788 + shutdown_required)
  1789 + ).
  1790 +
  1791 + The above loop must be run in a separate virtual machine. This will be done just after
  1792 + the two servers are started.
  1793 +
  1794 +
  1795 +
  1796 +
  1797 +
  1798 + *** [2] Tools.
  1799 +
  1800 + *** [2.1] Directories.
  1801 +
  1802 + We need a tool for creating directories (if needed).
  1803 +
  1804 + (This tool has been moved to 'tools/basis.anubis').
  1805 +
  1806 +
  1807 +
  1808 +
  1809 + *** [2.2] Secondary documents.
  1810 +
  1811 + Some HTML elements (like '<object>', '<frame>') cannot receive their content directly
  1812 + from the current document, but only through an URL. For this reason, we implement a
  1813 + mecanism for creating secondary documents on the fly. To that end we use the 'private
  1814 + download' mecanism.
  1815 +
  1816 + A secondary document is formated by the same functions as the main document itself. The
  1817 + next function takes an 'off form' element, creates the file containing the secondary
  1818 + document in HTML format, and returns the URL at which the document will be available.
  1819 +
  1820 +define String
  1821 + create_secondary_document
  1822 + (
  1823 + String sd, // site directory
  1824 + String as, // authorization_secret
  1825 + String sn, // state name
  1826 + $T -> Printable_tree format_element,
  1827 + $T content,
  1828 + HTML_Size width
  1829 + ) =
  1830 + with private_download_directory = sd+"/private_download",
  1831 + hash = web_arg_encode(sha1(content)),
  1832 + file_content = (Printable_tree)
  1833 + [doctype_w3c_header,
  1834 + "<html><body><table ",
  1835 + if width is
  1836 + {
  1837 + absolute(w) then ["width=\"",w-25],
  1838 + percentage(w) then ["width=\"95%\""]
  1839 + },"\"><tbody><tr><td align=right>",
  1840 + format_element(content),
  1841 + "</td></tr></tbody></table></body></html>"
  1842 + ],
  1843 + file_name = "sd"+hash+".html",
  1844 + file_path = private_download_directory+"/"+file_name,
  1845 + if write_to_file(file_path,file_content) is
  1846 + {
  1847 + cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",
  1848 + write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",
  1849 + ok then file_name+"?zauth="+
  1850 + make_authorization(sd,as,private_download_directory+"/"+file_name)
  1851 + }.
  1852 +
  1853 +
  1854 +
  1855 +
  1856 + *** [2.3] Generating unique ids.
  1857 +
  1858 + In order to uniquely name object for JavaScript we generate unique ids from a counter.
  1859 +
  1860 +define Int32
  1861 + new_idnum
  1862 + (
  1863 + Var(Int32) ic_v // 'idnum' counter variable
  1864 + ) =
  1865 + protect
  1866 + with result = *ic_v+1,
  1867 + ic_v <- result;
  1868 + result.
  1869 +
  1870 +
  1871 +
  1872 +
  1873 +
  1874 +
  1875 + *** [3] Managing web arguments.
  1876 +
  1877 + Web arguments are those pairs 'name=value' which are transmitted through the HTTP
  1878 + protocol. We need precise naming conventions for these web arguments.
  1879 +
  1880 +
  1881 +
  1882 + *** [3.1] Prefixing web arguments names.
  1883 +
  1884 + We want to assign different roles to web arguments, and we also want to be able to
  1885 + recognize its role directly from the name of a web argument. The name "s" is reserved
  1886 + for the web argument whose value is the name of the current state. The name "a" is
  1887 + reserved for the web argument whose value is the name of the action to be
  1888 + performed. Other web arguments receive arbitrary names, and in order to avoid clashes,
  1889 + these names are prefixed by:
  1890 +
  1891 + "p" for names of password inputs,
  1892 + "o" for other web arguments
  1893 +
  1894 + The reason why password input names have a distinct prefix is that this allows the HTTP
  1895 + server to hide the passwords on the console of the server and in the journal.
  1896 +
  1897 +
  1898 +
  1899 +
  1900 + *** [3.2] Separating web arguments.
  1901 +
  1902 + When a new request arrives, we need to separate the web arguments, that is to say:
  1903 +
  1904 + - find the value of "s", and recover the corresponding state,
  1905 + - find the value of "a", which is the name of the action to be performed,
  1906 + - get the list of all the remaining web arguments (operands of the action).
  1907 +
  1908 + We must also determine if the previous state may be recovered. If it is not the case
  1909 + (either because the previous state name is invalid, or the previous state is out of
  1910 + date), we must check if there is an action name. Indeed, the presence of an action name
  1911 + indicates that the user has clicked on one of our buttons or links. If on the contrary
  1912 + there is no action name the user has just entered our address in his browser. In this
  1913 + last case, we must send the first page of our site (maybe a 'login' page), but if there
  1914 + is an action, we must send a page just saying that the session ticket has expired. If
  1915 + the previous state is recovered and there is no action, the new state is the same as
  1916 + the previous state.
  1917 +
  1918 + The result of the separation of the web arguments is of type:
  1919 +
  1920 +type Separated_Web_Args($State):
  1921 + swa(Maybe(PreviousState($State)) previous_state,
  1922 + Maybe(String) action_name,
  1923 + List(Web_arg) operands).
  1924 +
  1925 +
  1926 +
  1927 + The next function constructs the function which separates the web arguments.
  1928 +
  1929 +define (List(Web_arg) lwa) -> Separated_Web_Args($State)
  1930 + make_separate_web_args_function
  1931 + (
  1932 + String state_directory,
  1933 + String -> PreviousState($State) retrieve_state
  1934 + ) =
  1935 + (List(Web_arg) lwa) |-swaf->
  1936 + if lwa is
  1937 + {
  1938 + [ ] then
  1939 + //
  1940 + // no web arg found => no previous state and no action
  1941 + //
  1942 + swa(failure,failure,[]),
  1943 +
  1944 + [wa_1 . wa_others] then
  1945 + //
  1946 + // at least one web arg =>
  1947 + // separate other web args, and insert the first one as needed
  1948 + //
  1949 + if (Separated_Web_Args($State))swaf(wa_others) is
  1950 + {
  1951 + swa(ps1, // possible previous state
  1952 + an1, // maybe an action name
  1953 + op1) // operands so far
  1954 + then
  1955 + if wa_1 is
  1956 + {
  1957 + web_arg(n,v) then
  1958 + with prefix = if substr(n,0,4) = "amp;" then substr(n,4,1) else substr(n,0,1),
  1959 + name_start = (Int32)(if substr(n,0,4) = "amp;" then 5 else 1),
  1960 + if prefix = "s" then
  1961 + swa(success(retrieve_state(v)),an1,op1) else
  1962 + if prefix = "a" then
  1963 + swa(ps1,success(v),op1) else
  1964 + if prefix = "t" then
  1965 + swa(ps1,an1,[web_arg("target",v) . op1]) else
  1966 + if prefix = "p" then
  1967 + swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
  1968 + if prefix = "o" then
  1969 + swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
  1970 + swa(ps1,an1,op1),
  1971 +
  1972 + upload(n,v,t) then
  1973 + swa(ps1,an1,[upload(substr(n,1,length(n)-1),v,t) . op1])
  1974 + }}
  1975 + }.
  1976 +
  1977 +
  1978 +
  1979 +
  1980 +
  1981 +
  1982 + *** [3.3] Applying an action.
  1983 +
  1984 + When the web arguments are separated (and their names cleaned up from prefixes), we may
  1985 + apply the action to the operands and the current state. We search for the action to be
  1986 + applied in the list of actions. If no action is found, the new state is the same as
  1987 + the previous state. Also, we deny the application of an HTTP action if the request
  1988 + arrives through the HTTPS channel and conversely.
  1989 +
  1990 +
  1991 +define (Maybe($State) previous,
  1992 + String action_name,
  1993 + HTTP_Info http_info,
  1994 + List(Web_arg) lwa,
  1995 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header))
  1996 + make_apply_action_function
  1997 + (
  1998 + List(Web_Action($SessionTicket, $State)) actions
  1999 + ) =
  2000 + with f =
  2001 + (Maybe($State) previous,
  2002 + String action_name,
  2003 + HTTP_Info http_info,
  2004 + List(Web_arg) lwa,
  2005 + Bool is_https,
  2006 + List(Web_Action($SessionTicket, $State)) actions) |-f->
  2007 + if actions is
  2008 + {
  2009 + [ ] then (print("action '"+action_name+
  2010 + "' not found.\n"); (failure, previous, [])),
  2011 + [ac1 . others] then if ac1 is
  2012 + {
  2013 + http_action(an,allow,do_it) then
  2014 + if an = action_name
  2015 + then if is_https
  2016 + then (print("HTTP action '"+an+
  2017 + "' called through HTTPS (denied).\n");
  2018 + (failure, previous, []))
  2019 + else if allow(previous)
  2020 + then do_it(http_info,lwa,previous)
  2021 + else (failure, previous, [])
  2022 + else f(previous,action_name,http_info,lwa,is_https,others),
  2023 +
  2024 + https_action(an,allow,do_it) then
  2025 + if an = action_name
  2026 + then if is_https
  2027 + then if allow(previous)
  2028 + then do_it(http_info,lwa,previous)
  2029 + else (failure, previous, [])
  2030 + else (print("HTTPS action '"+an+
  2031 + "' called through HTTP (denied).\n");
  2032 + (failure, previous, []))
  2033 + else f(previous,action_name,http_info,lwa,is_https,others),
  2034 +
  2035 + http_https_action(an,allow,do_it) then
  2036 + if an = action_name
  2037 + then if allow(previous)
  2038 + then do_it(http_info,lwa,previous)
  2039 + else (failure, previous, [])
  2040 + else f(previous,action_name,http_info,lwa,is_https,others),
  2041 +
  2042 + }
  2043 + },
  2044 + (Maybe($State) previous,
  2045 + String action_name,
  2046 + HTTP_Info http_info,
  2047 + List(Web_arg) lwa,
  2048 + Bool is_https) |->
  2049 + f(previous,action_name,http_info,lwa,is_https,actions).
  2050 +
  2051 +
  2052 +
  2053 +
  2054 +
  2055 +
  2056 +
  2057 +
  2058 + *** [4] Web site descriptions and the 'awp handlers'.
  2059 +
  2060 + *** [4.1] The type 'Web_Site'.
  2061 +
  2062 + The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We
  2063 + need another one, because, we have some extra informations to record for each site.
  2064 +
  2065 +public type Web_Site:
  2066 + web_site((Int32,Int32) -> Web_Site_Description description,
  2067 + List(String) -> One delete_out_of_date).
  2068 +
  2069 +
  2070 +
  2071 +
  2072 + *** [4.2] Making a web site description.
  2073 +
  2074 + Below is the function which creates a web site description. It first creates (if
  2075 + needed) the directories for the site, then constructs the tool functions for the site,
  2076 + and the site handler. Finally, it constructs the web site description.
  2077 +
  2078 + We gather common (constant) informations in the following type:
  2079 +
  2080 +type CommonInfo:
  2081 + info(String common_name,
  2082 + Int32 http_port,
  2083 + Int32 https_port,
  2084 + String site_directory,
  2085 + String authorization_secret
  2086 + ).
  2087 +
  2088 + We need a forward declaration.
  2089 +
  2090 +public define Printable_tree
  2091 + format
  2092 + (
  2093 + CommonInfo cinfo,
  2094 + String state_name,
  2095 + HTML_Page page,
  2096 + Bool is_https,
  2097 + String charset
  2098 + ).
  2099 +
  2100 +
  2101 +define Printable_tree
  2102 + format
  2103 + (
  2104 + HTML_Size s
  2105 + ) =
  2106 + if s is
  2107 + {
  2108 + absolute(x) then ["\"",x,"\""],
  2109 + percentage(x) then ["\"",x,"%\""]
  2110 + }.
  2111 +
  2112 +define Printable_tree
  2113 + top_redirection_page
  2114 + (
  2115 + String common_name
  2116 + ) =
  2117 + [ doctype_w3c_header,
  2118 + "<html><head>",
  2119 + "<meta http-equiv=\"Refresh\" content=\"0; URL=javascript:void window.open('/?t=','_top')\" />",
  2120 + "</head><body></body></html>"
  2121 + ].
  2122 +
  2123 +public define Web_Site
  2124 + make_web_site_description
  2125 + (
  2126 + List(String) common_names, // for example: ["www.our-business.com"]
  2127 + String site_directory,
  2128 + String state_directory,
  2129 + One -> One init,
  2130 + (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
  2131 + ($State expired,
  2132 + HTTP_Info,
  2133 + List(Web_arg),
  2134 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
  2135 + (HTTP_Info,
  2136 + List(Web_arg),
  2137 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
  2138 + List(Web_Action($SessionTicket, $State)) actions,
  2139 + (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
  2140 + Int32 timeout,
  2141 + List(Redirection) redirections,
  2142 + String charset,
  2143 + List(String) journal_extensions,
  2144 + List(String) journal_headers,
  2145 + String secret,
  2146 + List(MIME) known_mime_types,
  2147 + (String action_name,
  2148 + List(Web_arg) args) -> One before_send_file
  2149 + ) =
  2150 + init(unique);
  2151 +
  2152 +
  2153 + //
  2154 + // make required directories (if needed)
  2155 + //
  2156 + with web_sites_directory = make_directory(my_anubis_directory+"/web_sites"),
  2157 + base_directory = make_directory(site_directory),
  2158 + // state_directory = make_directory(site_directory+"/states"),
  2159 + forget(make_directory(site_directory+"/public"));
  2160 + //
  2161 + // construct tool functions
  2162 + //
  2163 + with save_state = make_save_state_function(timeout,state_directory),
  2164 + retrieve_state = make_retrieve_state_function(state_directory),
  2165 + separate_web_args = make_separate_web_args_function(state_directory,retrieve_state),
  2166 + apply_action = make_apply_action_function(actions),
  2167 + //
  2168 + // construct the site handler
  2169 + //
  2170 + site_handler = (Int32 http_port, Int32 https_port) |->
  2171 + ((String host_name,
  2172 + HTTP_Info http_info,
  2173 + List(Web_arg) lwa,
  2174 + Bool is_https) |->
  2175 + ((List(HTTP_header),Printable_tree))
  2176 + if separate_web_args(lwa) is
  2177 + {
  2178 + swa(mb_previous_state,mb_action_name,operands) then
  2179 + with state_and_headers = if mb_previous_state is
  2180 + {
  2181 + failure then
  2182 + if mb_action_name is
  2183 + {
  2184 + failure then initial_state(http_info),
  2185 + success(action_name) then
  2186 + apply_action(failure,action_name,http_info,operands,is_https)
  2187 + },
  2188 + success(previous_state) then if previous_state is
  2189 + {
  2190 + not_found then
  2191 + if mb_action_name is
  2192 + {
  2193 + failure then initial_state(http_info),
  2194 + success(_) then
  2195 + ticket_lost_state(http_info,lwa,is_https)
  2196 + },
  2197 +
  2198 + out_of_date(state) then
  2199 + ticket_expired_state(state,http_info,lwa,is_https),
  2200 +
  2201 + still_valid(state) then
  2202 + if mb_action_name is
  2203 + {
  2204 + failure then (failure, success(state), []),
  2205 + success(action_name) then
  2206 + apply_action(success(state),action_name,http_info,operands,is_https)
  2207 + }
  2208 + }
  2209 + },
  2210 + if state_and_headers is (session_ticket, mb_new_state, headers) then
  2211 + with state_name = save_state(mb_new_state),
  2212 + (headers,
  2213 + format(info(host_name, http_port, https_port, site_directory, secret),
  2214 + state_name,
  2215 + compute_page(session_ticket, mb_new_state),
  2216 + is_https,
  2217 + charset))
  2218 + }),
  2219 + //
  2220 + // make the delete_out_of_date function
  2221 + //
  2222 + delete_out_of_date =
  2223 + make_delete_out_of_date_states_function((Maybe($State))failure,
  2224 + site_directory+"/states"),
  2225 + //
  2226 + // construct the web site description
  2227 + //
  2228 + web_site((Int32 http_port, Int32 https_port) |->
  2229 + web_site_description(common_names,
  2230 + site_directory,
  2231 + redirections,
  2232 + charset,
  2233 + journal_extensions,
  2234 + journal_headers,
  2235 + secret,
  2236 + known_mime_types,
  2237 + site_handler(http_port,https_port),
  2238 + (List(Web_arg) lwa) |-> if separate_web_args(lwa) is
  2239 + swa(mb_previous_state,mb_action_name,operands) then
  2240 + if mb_action_name is
  2241 + {
  2242 + failure then unique
  2243 + success(an) then before_send_file(an,operands)
  2244 + }),
  2245 + delete_out_of_date).
  2246 +
  2247 +
  2248 +
  2249 +
  2250 + *** [4.3] Starting the servers.
  2251 +
  2252 +public define Start_Web_Sites_Result
  2253 + start_web_sites
  2254 + (
  2255 + Int32 ip_address, // the IP address shared by the web sites
  2256 + Int32 http_port, // usually: 80
  2257 + Int32 https_port, // usually: 443
  2258 + String ssl_certificate_common_name,
  2259 + List(Web_Site) web_sites, // web sites to be started
  2260 + Var(Bool) shutdown_required
  2261 + ) =
  2262 + with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),
  2263 + with http_server_r =
  2264 + start_http_server(ip_address,http_port,
  2265 + map(get_description,web_sites),
  2266 + load_denial_of_service_info),
  2267 + with https_server_r =
  2268 + start_https_server(ip_address,https_port,
  2269 + ssl_certificate_common_name,
  2270 + map(get_description,web_sites),
  2271 + load_denial_of_service_info),
  2272 + if http_server_r is ok(http_server)
  2273 + then
  2274 + (
  2275 + if https_server_r is ok(https_server)
  2276 + then
  2277 + (
  2278 + start_http_servers_tasks(map(get_description,web_sites),
  2279 + [http_server,https_server],
  2280 + 600); // period of 10 minutes
  2281 + delegate
  2282 + delete_states_loop(
  2283 + map((Web_Site ws) |->
  2284 + (site_directory(description(ws)(http_port,https_port))+
  2285 + "/states",delete_out_of_date(ws)),
  2286 + web_sites),
  2287 + 3600*24*3, // keep out of date states 3 days
  2288 + now,
  2289 + http_server,
  2290 + https_server,
  2291 + shutdown_required),
  2292 + ok(http_server,https_server)
  2293 + )
  2294 + else cannot_bind_to_port(https_port)
  2295 + )
  2296 + else
  2297 + (
  2298 + if https_server_r is ok(https_server)
  2299 + then cannot_bind_to_port(http_port)
  2300 + else cannot_bind_to_port(http_port,https_port)
  2301 + ).
  2302 +
  2303 +
  2304 +public define One
  2305 + start_web_sites
  2306 + (
  2307 + Int32 ip_address, // the IP address shared by the web sites
  2308 + Int32 http_port, // usually: 80
  2309 + Int32 https_port, // usually: 443
  2310 + String ssl_certificate_common_name,
  2311 + List(Web_Site) web_sites, // web sites to be started
  2312 + Var(Bool) shutdown_required,
  2313 + Logger log
  2314 + ) =
  2315 + if (Start_Web_Sites_Result)start_web_sites(ip_address,
  2316 + http_port,
  2317 + https_port,
  2318 + ssl_certificate_common_name,
  2319 + web_sites,
  2320 + shutdown_required) is
  2321 + {
  2322 + cannot_bind_to_port(n) then logError(log, "Cannot bind to port: "+n),
  2323 + cannot_bind_to_port(n,m) then logError(log, "Cannot bind to ports: "+n+", "+m),
  2324 + ok(s1,s2) then logInfo(log, "Servers started.")
  2325 + }.
  2326 +
  2327 +
  2328 +
  2329 +
  2330 +
  2331 + *** [5] HTML Formating.
  2332 +
  2333 + We need to translate HTML elements as defined above into actual HTML text.
  2334 +
  2335 + Actioners require special informations, which must be transmitted when needed by the
  2336 + 'format' functions:
  2337 +
  2338 + - the 'common name', which is used for URLs,
  2339 + - the HTTP/HTTPS port number,
  2340 + - the 'state name', which must be transmitted when the actioner is clicked upon,
  2341 + - the 'form name' (if any) to which the actioner refers.
  2342 +
  2343 + If the actioner is off form, and if it refers to a form, the name of that form is
  2344 + already known by the actioner. On the contrary, if the actioner is 'in form', it refers
  2345 + implicitly to the form containing it. The name of that form is transmitted to the
  2346 + 'format' functions called from within the formating of that form.
  2347 +
  2348 +
  2349 +
  2350 +
  2351 + *** [5.1] The type 'HTML_Any($T)'.
  2352 +
  2353 + The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The
  2354 + parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.
  2355 +
  2356 +type HTML_Any($T):
  2357 + any_text (List(Text_Option), String the_text),
  2358 + any_preformated (List(Text_Option), String),
  2359 + any_paragraph (List(Text_Option), $T content),
  2360 + any_image (String url),
  2361 + any_image (String url, Int32 width, Int32 height),
  2362 + any_table (List(Table_Option), HTML_Header_Row($T), List(HTML_Row($T))),
  2363 + any_center ($T),
  2364 + any_mail_to (String email, $T element),
  2365 + any_scroller (Int32 width, Int32 height,
  2366 + Int32 content_width, Int32 content_height,
  2367 + $T content),
  2368 + any_fixed_size (HTML_Size width, HTML_Size height, $T content),
  2369 + any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  2370 + any_actioner (Actioner_Connection,
  2371 + Actioner_Target,
  2372 + Actioner_Aspect,
  2373 + String action_name,
  2374 + List((String,String)) extra_ops,
  2375 + List(Actioner_Local_Action),
  2376 + Maybe(String) form_name),
  2377 + any_foreign_link (List(Text_Option), String url),
  2378 + any_foreign_link (List(Text_Option), String url, String name),
  2379 + any_private_download (String abs_path, String name, String extra_ext,
  2380 + Maybe((String,List((String,String))))),
  2381 + any_div (List(CoreAttrs), $T element),
  2382 + any_div_empty (List(CoreAttrs)),
  2383 + any_coreattrs (List(CoreAttrs)).
  2384 +
  2385 +
  2386 +
  2387 + *** [5.2] Formating a color.
  2388 +
  2389 + RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters
  2390 + hexadecimal values.
  2391 +
  2392 +define String
  2393 + html_format
  2394 + (
  2395 + RGB color
  2396 + ) =
  2397 + if color is rgb(r,g,b) then
  2398 + "#" + hexadecimal(word8_to_int32(r),2)
  2399 + + hexadecimal(word8_to_int32(g),2)
  2400 + + hexadecimal(word8_to_int32(b),2).
  2401 +
  2402 +
  2403 + The following is a very arbitrary definition of the opposite color. The thing which is
  2404 + important is that it is far from the original, so that characters in 'opposite' color
  2405 + are clearly visible over the original.
  2406 +
  2407 +define RGB
  2408 + opposite
  2409 + (
  2410 + RGB color
  2411 + ) =
  2412 + if color is rgb(r,g,b) then
  2413 + with r1 = word8_to_int32(r),
  2414 + with g1 = word8_to_int32(g),
  2415 + with b1 = word8_to_int32(b),
  2416 + rgb(truncate_to_word8(255-r1),
  2417 + truncate_to_word8(255-g1),
  2418 + truncate_to_word8(255-b1)).
  2419 +
  2420 +
  2421 +
  2422 +
  2423 + *** [5.3] Creating buttons.
  2424 +
  2425 + We want to be able to create buttons in the form of a pair of images (rollovers)
  2426 + automatically. We use the JPEG interface, because for the time being Anubis cannot
  2427 + handle other kinds of images.
  2428 +
  2429 +
  2430 + Computing printed text length.
  2431 +
  2432 + define Int32
  2433 + printed_text_width
  2434 + (
  2435 + Word8 -> Int32 char_size,
  2436 + List(Word8) l
  2437 + ) =
  2438 + if l is
  2439 + {
  2440 + [] then (Int32) 0,
  2441 + [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)
  2442 + }.
  2443 +
  2444 + define Int32
  2445 + printed_text_width
  2446 + (
  2447 + SystemFont font,
  2448 + String s
  2449 + ) =
  2450 + printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),
  2451 + explode(s)).
  2452 +
  2453 +
  2454 +
  2455 + Converting RGB to RGBA.
  2456 +
  2457 +define RGBA
  2458 + to_rgba
  2459 + (
  2460 + RGB color
  2461 + ) =
  2462 + if color is rgb(r,g,b) then rgba(r,g,b,255).
  2463 +
  2464 +
  2465 + Drawing a 'relief'.
  2466 +
  2467 + define One
  2468 + draw_relief
  2469 + (
  2470 + RGBAImage dest,
  2471 + RGBA color,
  2472 + Int32 contrast,
  2473 + Int32 x,
  2474 + Int32 y,
  2475 + Int32 width,
  2476 + Int32 height
  2477 + ) =
  2478 + with l = lighten(color,contrast),
  2479 + d = darken(color,contrast),
  2480 + draw_rectangle(dest,rect(x,y,x+width,y+1),l);
  2481 + draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);
  2482 + draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);
  2483 + draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).
  2484 +
  2485 +
  2486 + Creating a button background.
  2487 +
  2488 + define RGBAImage
  2489 + create_button_background
  2490 + (
  2491 + RGBA color,
  2492 + Int32 width,
  2493 + Int32 height
  2494 + ) =
  2495 + with result = create_rgba_image(width,height,color),
  2496 + draw_relief(result,color,100,0,0,width,height);
  2497 + draw_relief(result,color,70,1,1,width-2,height-2);
  2498 + draw_relief(result,color,55,2,2,width-4,height-4);
  2499 + draw_relief(result,color,35,3,3,width-6,height-6);
  2500 + draw_relief(result,color,20,4,4,width-8,height-8);
  2501 + draw_relief(result,color,10,5,5,width-10,height-10);
  2502 + draw_relief(result,color,5,6,6,width-12,height-12);
  2503 + result.
  2504 +
  2505 +
  2506 + Drawing the text over the background.
  2507 +
  2508 + define One
  2509 + draw_button_text
  2510 + (
  2511 + RGBAImage image,
  2512 + String text,
  2513 + Int32 text_index,
  2514 + Int32 pixel_x,
  2515 + Int32 y,
  2516 + Rectangle clip,
  2517 + RGBA color,
  2518 + SystemFont font,
  2519 + ) =
  2520 + if nth(text_index,text) is
  2521 + {
  2522 + failure then unique,
  2523 + success(c) then
  2524 + with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),
  2525 + draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)
  2526 + }.
  2527 +
  2528 + define One
  2529 + draw_button_text
  2530 + (
  2531 + RGBAImage image,
  2532 + String text,
  2533 + Int32 text_width,
  2534 + RGBA light_color,
  2535 + RGBA dark_color,
  2536 + SystemFont font
  2537 + ) =
  2538 + with image_width = width(image),
  2539 + image_height = height(image),
  2540 + x_pos = (image_width-text_width)>>1,
  2541 + clip = rect(0,0,image_width,image_height),
  2542 + new_light_color = lighten(light_color,150),
  2543 + new_dark_color = darken(dark_color,40),
  2544 + draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);
  2545 + draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).
  2546 +
  2547 +
  2548 + The next function creates the two images for a button. The information given is the
  2549 + main color of the button, the text of the button and the minimal width (in pixels) of
  2550 + the button. The function does not create the button if the images already exist. The
  2551 + two images are stored in the directory 'site_directory/buttons'. The names of the files
  2552 + are of the form:
  2553 +
  2554 + bxxxx_off.jpg
  2555 + bxxxx_on.jpg
  2556 +
  2557 + where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like
  2558 + 'rm'), and where 'xxxx' is created from the given informations by the formula:
  2559 +
  2560 + xxxx = web_arg_encode(sha1((color,text,width)))
  2561 +
  2562 + Hence, distinct informations give distinct file names.
  2563 +
  2564 +
  2565 + define String // returns xxxx
  2566 + create_button_images
  2567 + (
  2568 + String site_directory,
  2569 + RGBA color,
  2570 + String text,
  2571 + Int32 width,
  2572 + SystemFont font
  2573 + ) =
  2574 + with xxxx = web_arg_encode(sha1((color,text,width))),
  2575 + buttons_dir = site_directory+"/public/buttons",
  2576 + off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",
  2577 + on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",
  2578 + if file_exists(on_filepath)
  2579 + then xxxx
  2580 + else with
  2581 + text_width = printed_text_width(font,text),
  2582 + button_width = max(width,text_width+12),
  2583 + button_height = (Int32)20,
  2584 + light_color = lighten(color,60),
  2585 + very_light_color = lighten(light_color,30),
  2586 + dark_color = darken(color,40),
  2587 + background_off =
  2588 + create_button_background(color,button_width,button_height),
  2589 + background_on =
  2590 + create_button_background(light_color,button_width,button_height),
  2591 +
  2592 + draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);
  2593 + draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);
  2594 + forget(write_image_to_JPEG_file(to_JPEG(background_off),
  2595 + off_filepath,
  2596 + 100));
  2597 + forget(write_image_to_JPEG_file(to_JPEG(background_on),
  2598 + on_filepath,
  2599 + 100));
  2600 + xxxx.
  2601 +
  2602 +
  2603 +
  2604 +
  2605 +
  2606 + *** [5.4] Formating an actioner.
  2607 +
  2608 + An actioner works as follows. Assume first that it refers to a form. When it is clicked
  2609 + upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the
  2610 + form, and submits the form, using the JavaScript command 'form_name.submit()'. If the
  2611 + actioner does not refer to a form, it fires the URL directly via 'href', because in
  2612 + that case, the actioner is always an <a> tag.
  2613 +
  2614 + The URL itself is composed using the connection sort (same, http or https), the common
  2615 + name and port number (if needed), the state name, the action name, and the extra
  2616 + operands, which are put into a query string. It may look like this:
  2617 +
  2618 + http://common_name:port/?s=state_name&a=action_name&oname=value...
  2619 +
  2620 + Each extra operand is a pair of strings: (name,value). It is formated as:
  2621 +
  2622 + &oname=value
  2623 +
  2624 +
  2625 +define String
  2626 + format_extra_operands
  2627 + (
  2628 + List((String,String)) l
  2629 + ) =
  2630 + if l is
  2631 + {
  2632 + [ ] then "",
  2633 + [h . t] then if h is (n,v) then
  2634 + "&amp;o"+n+"="+v+format_extra_operands(t)
  2635 + }.
  2636 +
  2637 +
  2638 + It seams that the standard requires "&amp;" instead of "&" !
  2639 +
  2640 + In case the target is another window, we need to format the options for this window.
  2641 +
  2642 +define String
  2643 + format
  2644 + (
  2645 + List(Other_Window_Option) l
  2646 + ) =
  2647 + if l is
  2648 + {
  2649 + [ ] then "",
  2650 + [h . t] then if h is
  2651 + {
  2652 + resizable then "resizable",
  2653 + scrollbars then "scrollbars",
  2654 + width(w) then "width="+w,
  2655 + height(h) then "height="+h
  2656 + } + if t is [ ] then "" else (","+format(t))
  2657 + }.
  2658 +
  2659 +
  2660 +
  2661 + Formating choices for a <select> tag.
  2662 +
  2663 +public define Printable_tree
  2664 + format_choices
  2665 + (
  2666 + List(String) l
  2667 + ) =
  2668 + if l is
  2669 + {
  2670 + [ ] then [ ],
  2671 + [h . t] then ["<option>", h, "</option>\n" . format_choices(t)]
  2672 + }.
  2673 +
  2674 +
  2675 +public define Printable_tree
  2676 + format_choices
  2677 + (
  2678 + List(String) l,
  2679 + InitialValue selected
  2680 + ) =
  2681 + if l is
  2682 + {
  2683 + [ ] then [ ],
  2684 + [h . t] then if h = selected.value
  2685 + then ["<option selected>", h, "</option>\n" . format_choices(t)]
  2686 + else ["<option>", h, "</option>\n" . format_choices(t,selected)]
  2687 + }.
  2688 +
  2689 +
  2690 +public define Printable_tree
  2691 + format_choices
  2692 + (
  2693 + List((WebArgValue,String)) l
  2694 + ) =
  2695 + if l is
  2696 + {
  2697 + [ ] then [ ],
  2698 + [h . t] then
  2699 + if h is (val,item)
  2700 + then ["<option value=\""+val.value+"\">", item, "</option>\n" . format_choices(t)]
  2701 + }.
  2702 +
  2703 +public define Printable_tree
  2704 + format_choices
  2705 + (
  2706 + List((WebArgValue,String)) l,
  2707 + InitialValue selected
  2708 + ) =
  2709 + if l is
  2710 + {
  2711 + [ ] then [ ],
  2712 + [h . t] then
  2713 + if h is (val,item) then
  2714 + if val.value = selected.value
  2715 + then ["<option value=\""+val.value+"\" selected=\"selected\">", item, "</option>\n" . format_choices(t)]
  2716 + else ["<option value=\""+val.value+"\">", item, "</option>\n" . format_choices(t,selected)]
  2717 + }.
  2718 +
  2719 +
  2720 +
  2721 +variable Int32 count = 0.
  2722 +
  2723 + Note: this counter is private to the virtual machine, hence there is one counter by
  2724 + client and by page : it seems that a new VM (simply a delegate) is started for each request.
  2725 +
  2726 +define Int32
  2727 + new_count
  2728 + =
  2729 + count <- *count+1;
  2730 + *count.
  2731 +
  2732 +
  2733 + The next function composes the URL. It is a JavaScript URL when the target is another
  2734 + window.
  2735 +
  2736 +define String
  2737 + make_actioner_url
  2738 + (
  2739 + CommonInfo cinfo,
  2740 + Actioner_Connection connection,
  2741 + Actioner_Target target,
  2742 + String state_name,
  2743 + String action_name,
  2744 + List((String,String)) extra_ops,
  2745 + Bool is_https
  2746 + ) =
  2747 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  2748 + with strict_url =
  2749 + if connection is
  2750 + {
  2751 + same then "/",
  2752 + /*
  2753 + same then if is_https
  2754 + then "https://"+common_name+":"+https_port+"/"
  2755 + else "http://"+common_name+":"+https_port+"/",
  2756 + */
  2757 +
  2758 + http then "http://"+common_name+":"+http_port+"/",
  2759 + https then "https://"+common_name+":"+https_port+"/",
  2760 + } +
  2761 + "?s=" + state_name + "&amp;a=" + action_name +
  2762 + format_extra_operands(extra_ops),
  2763 + if target is
  2764 + {
  2765 + same then strict_url,
  2766 + same(label) then strict_url+"#"+label,
  2767 + other(wn,ops) then
  2768 + "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+
  2769 + "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
  2770 + }.
  2771 +
  2772 +
  2773 + Depending on the fact that the actioner refers to a form or not, the URL is used in two
  2774 + different ways. If the actioner does not refer to a form, it is realized by a '<a>'
  2775 + tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'
  2776 + tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'
  2777 + event handler. The handler calls a JavaScript function which puts the URL as the value
  2778 + of the 'action' attribute of the form, and submits the form.
  2779 +
  2780 +type URL_or_JavaScript:
  2781 + url (String),
  2782 + javascript (Printable_tree script, Printable_tree handler).
  2783 +
  2784 +
  2785 + It was as shown below, in order to allow submission of a form from outside the form,
  2786 + but this makes problems:
  2787 +
  2788 +define URL_or_JavaScript
  2789 + format_action
  2790 + (
  2791 + String the_url,
  2792 + Maybe(String) mb_id,
  2793 + Maybe(String) mb_form_name
  2794 + ) =
  2795 + if mb_form_name is
  2796 + {
  2797 + failure then
  2798 + url(the_url),
  2799 + success(form_name) then
  2800 + with n = new_count,
  2801 + fn_name = "pfu_" + if mb_id is
  2802 + {
  2803 + failure then form_name + n,
  2804 + success(id) then id,
  2805 + },
  2806 + javascript(
  2807 + [
  2808 + "<script type=\"text/javascript\" language =\"JavaScript\">\n",
  2809 + "function ", fn_name, "() {\n",
  2810 + " var fa = document.forms.f",form_name,";\n",
  2811 + " var u = \"",the_url,"\";\n",
  2812 + " fa.action = u;\n",
  2813 + " fa.submit(); }",
  2814 + "</script>\n"
  2815 + ],
  2816 + [fn_name,"();"]
  2817 + )
  2818 + }.
  2819 +
  2820 +
  2821 +
  2822 + Now, we format the actioner according to its aspect.
  2823 +
  2824 +define List(Text_Option)
  2825 + get_text_options
  2826 + (
  2827 + List(Text_Option) l
  2828 + )
  2829 + =
  2830 + if l is
  2831 + {
  2832 + [] then [],
  2833 + [h . t ] then
  2834 + if h is class(_) then
  2835 + get_text_options(t)
  2836 + else if h is id(_) then
  2837 + get_text_options(t)
  2838 + else
  2839 + [ h . get_text_options(t) ]
  2840 +
  2841 + }.
  2842 +
  2843 + /**
  2844 + * Extract the CSS class list from the list of Text_Option
  2845 + */
  2846 +define List(CoreAttrs)
  2847 + get_css_class
  2848 + (
  2849 + List(Text_Option) l
  2850 + )
  2851 + =
  2852 + if l is
  2853 + {
  2854 + [] then [],
  2855 + [h . t ] then
  2856 +
  2857 +
  2858 + if h is class(name) then
  2859 + [ class(name) . get_css_class(t) ]
  2860 + else if h is id(name) then
  2861 + [ id(name) . get_css_class(t) ]
  2862 + else
  2863 + get_css_class(t)
  2864 + }
  2865 + .
  2866 +
  2867 +define Printable_tree
  2868 + format
  2869 + (
  2870 + List(Actioner_Local_Action) l
  2871 + ) =
  2872 + if l is
  2873 + {
  2874 + [ ] then [ ],
  2875 + [h . t] then [if h is
  2876 + {
  2877 + close_window then [" window.close(); "]
  2878 + }
  2879 + . format(t)]
  2880 + }.
  2881 +
  2882 +public define String
  2883 + format_attrs
  2884 + (
  2885 + List(CoreAttrs) attributs
  2886 + )=
  2887 + if attributs is
  2888 + {
  2889 + [] then "",
  2890 + [h .t] then
  2891 + with current = if h is
  2892 + {
  2893 + id(id_name) then
  2894 + " id=\"" + id_name + "\"",
  2895 + class(class_name) then
  2896 + " class=\"" + class_name + "\"",
  2897 + style(style_string) then
  2898 + " style=\"" + style_string + "\"",
  2899 +
  2900 + tooltip(title_string) then
  2901 + " title=\"" + title_string + "\"",
  2902 +
  2903 + lang(lang) then
  2904 + " xml:lang=" + lang,
  2905 + dir(reading_Way) then
  2906 + if reading_Way is
  2907 + {
  2908 + ltr then " dir=ltr",
  2909 + rtl then " dir=rtl"
  2910 + },
  2911 +
  2912 + accesskey(key) then
  2913 + " accesskey=\"" + key + "\"",
  2914 + tabindex(index) then
  2915 + " tabindex=\"" + index + "\"",
  2916 +
  2917 + attr(name, value) then
  2918 + " " + name + "=\"" + value + "\"",
  2919 +
  2920 + event(e, value) then
  2921 + " " + event_name(e) + "=\"" + value + "\"",
  2922 + },
  2923 + current + format_attrs(t)
  2924 + }.
  2925 +
  2926 +define String
  2927 + format_attrs
  2928 + (
  2929 + List(InputAttrs) attributs
  2930 + )=
  2931 + if attributs is
  2932 + {
  2933 + [] then "",
  2934 + [h .t] then
  2935 + with current = if h is
  2936 + {
  2937 + class(class_name) then
  2938 + " class=\"" + class_name + "\"",
  2939 + style(style_string) then
  2940 + " style=\"" + style_string + "\"",
  2941 +
  2942 + tooltip(title_string) then
  2943 + " title=\"" + title_string + "\"",
  2944 +
  2945 + lang(lang) then
  2946 + " xml:lang=" + lang,
  2947 + dir(reading_Way) then
  2948 + if reading_Way is
  2949 + {
  2950 + ltr then " dir=ltr",
  2951 + rtl then " dir=rtl"
  2952 + },
  2953 +
  2954 + accesskey(key) then
  2955 + " accesskey=\"" + key + "\"",
  2956 + tabindex(index) then
  2957 + " tabindex=\"" + index + "\"",
  2958 +
  2959 + attr(name, value) then
  2960 + " " + name + "=\"" + value + "\"",
  2961 +
  2962 + event(e, value) then
  2963 + " " + event_name(e) + "=\"" + value + "\"",
  2964 + },
  2965 + current + format_attrs(t)
  2966 + }.
  2967 +
  2968 +define String
  2969 + format_text_options
  2970 + (
  2971 + List(Text_Option) l
  2972 + )
  2973 + =
  2974 + with text_options = get_text_options(l),
  2975 + css_classes = get_css_class(l),
  2976 + if text_options is
  2977 + {
  2978 + [] then "",
  2979 + [_._] then " style=\"" + format(text_options) + "\" "
  2980 + }
  2981 + +
  2982 + if css_classes is
  2983 + {
  2984 + [] then "",
  2985 + [_._] then format_attrs(css_classes)
  2986 + }.
  2987 +
  2988 +//define String
  2989 +// _format
  2990 +// (
  2991 +// List(CoreAttrs) opt
  2992 +// )=
  2993 +// if opt is
  2994 +// {
  2995 +// [] then "",
  2996 +// [h .t] then
  2997 +// with current = if h is
  2998 +// {
  2999 +// id(id_name) then
  3000 +// " id=\"" + id_name + "\"",
  3001 +// class(class_name) then
  3002 +// " class=\"" + class_name + "\"",
  3003 +// style(style_string) then
  3004 +// " style=\"" + style_string + "\"",
  3005 +//
  3006 +// title(title_string) then
  3007 +// " title=\"" + title_string + "\"",
  3008 +// lang(lang) then
  3009 +// " xml:lang=" + lang,
  3010 +// dir(reading_Way) then
  3011 +// if reading_Way is
  3012 +// {
  3013 +// ltr then " dir=ltr",
  3014 +// rtl then " dir=rtl"
  3015 +// },
  3016 +// attr(name, value) then
  3017 +// " " + name + "=\"" + value + "\"",
  3018 +// event(e, value) then
  3019 +// " " + event_name(e) + "=\"" + value + "\""
  3020 +// },
  3021 +// current + _format(t)
  3022 +// }
  3023 +// .
  3024 +
  3025 +define Printable_tree
  3026 + format_div_option
  3027 + (
  3028 + List(CoreAttrs) options
  3029 + )=
  3030 + ["<div" + format_attrs(options) + ">"] .
  3031 +
  3032 +
  3033 +define Maybe(String)
  3034 + extract_id
  3035 + (
  3036 + List(CoreAttrs) attrbs
  3037 + ) =
  3038 + if attrbs is
  3039 + {
  3040 + [] then failure,
  3041 + [h . t] then
  3042 + if h is id(value) then success(value)
  3043 + else extract_id(t)
  3044 + }.
  3045 +
  3046 +define Maybe(String)
  3047 + extract_id
  3048 + (
  3049 + Actioner_Aspect aspect
  3050 + ) =
  3051 + if aspect is
  3052 + {
  3053 + link(_,_) then failure,
  3054 + push_button(opts,_) then extract_id(opts),
  3055 + submit(opts,_) then extract_id(opts),
  3056 + button(_,_) then failure,
  3057 + button(_,_,_,_) then failure,
  3058 + immediate_selector(_,_,_) then failure,
  3059 + immediate_selector(_,_,_,_) then failure,
  3060 + }.
  3061 +
  3062 +define Printable_tree
  3063 + format_actioner
  3064 + (
  3065 + CommonInfo cinfo,
  3066 + String state_name,
  3067 + Actioner_Connection connection,
  3068 + Actioner_Target target,
  3069 + Actioner_Aspect aspect,
  3070 + String action_name,
  3071 + List((String,String)) extra_ops,
  3072 + List(Actioner_Local_Action) local_actions,
  3073 + Maybe(String) mb_form_name,
  3074 + Bool is_https,
  3075 + ) =
  3076 + if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  3077 + with url = make_actioner_url(cinfo,connection,target,
  3078 + state_name,action_name,extra_ops,is_https),
  3079 + with action = format_action(url, extract_id(aspect), mb_form_name),
  3080 + if aspect is
  3081 + {
  3082 +
  3083 + link(opt, text) then [
  3084 + if action is
  3085 + {
  3086 + url(u) then
  3087 + ["<a href=\"",u,"\"", format_text_options(opt), ">"],
  3088 +// ["<a href=\"",u,"\" style=\"",format(reverse(opt)),"\">"],
  3089 + javascript(s,h) then
  3090 + [s,"<a href=\"javascript: ",h,"\">"]
  3091 + },
  3092 + text,
  3093 + "</a>"
  3094 + ],
  3095 +
  3096 + push_button(options, text) then
  3097 + [
  3098 + if action is
  3099 + {
  3100 + url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",
  3101 + text, "</a>"
  3102 + ],
  3103 + javascript(s,h) then
  3104 + [s,"<input type=\"button\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]
  3105 + }
  3106 + ],
  3107 + submit(options, text) then
  3108 + [
  3109 + if action is
  3110 + {
  3111 + url(u) then ["<a href=\"",u,"\"", format_attrs(options), ">",
  3112 + text, "</a>"
  3113 + ],
  3114 + javascript(s,h) then
  3115 + if action_name = "" then // special case where URL (so the action_name) is provided by the form itself
  3116 + // so javascript isn't needed. This is the standard HMTL way.
  3117 + ["<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " />"]
  3118 + else
  3119 + [s,"<input type=\"submit\" value=\"",text,"\"", format_attrs(options), " onclick=\"",h,"\" />"]
  3120 + }
  3121 + ],
  3122 +
  3123 + button(url_off,url_on) then
  3124 + [ if action is
  3125 + {
  3126 + url(u) then ["<a href=\"",u],
  3127 + javascript(s,h) then [s,"<a onmousedown=\"",h]
  3128 + },
  3129 + "\" style=\"text-decoration:none\">",
  3130 + "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=\"0\"",
  3131 + " onmouseover=\"this.src='",url_on,"'\" ",
  3132 + " onmouseout=\"this.src='",url_off,"'\">",
  3133 + "</a>"
  3134 + ],
  3135 +
  3136 + button(url_off,url_on,w,h) then
  3137 + [ if action is
  3138 + {
  3139 + url(u) then ["<a href=\"",u],
  3140 + javascript(s,h) then [s,"<a onmousedown=\"",h]
  3141 + },
  3142 + "\" style=\"text-decoration:none\">",
  3143 + "<img width=\"",w,"\" height=\"",h,"\" alt=\"",url_off,"\" src=\"",url_off,"\" border=\"0\"",
  3144 + " onmouseover=\"this.src='",url_on,"'\" ",
  3145 + " onmouseout=\"this.src='",url_off,"'\">",
  3146 + "</a>"
  3147 + ],
  3148 +
  3149 + immediate_selector(name,size,choices) then
  3150 + [ if action is
  3151 + {
  3152 + url(u) then ["<select href=\"",u]
  3153 + javascript(s,h) then [s,"<select onchange=\"",h]
  3154 + },
  3155 + "\" name=\"o",name,"\" size=\"",size,"\">",
  3156 + format_choices(choices),"</select>"
  3157 + ],
  3158 + immediate_selector(name,size,choices,selected) then
  3159 + [ if action is
  3160 + {
  3161 + url(u) then ["<select href=\"",u]
  3162 + javascript(s,h) then [s,"<select onchange=\"",h]
  3163 + },
  3164 + "\" name=\"o",name,"\" size=\"",size,"\">",
  3165 + format_choices(choices, selected),"</select>"
  3166 + ],
  3167 + }.
  3168 +
  3169 +
  3170 +define Printable_tree
  3171 + format_local_popup_button
  3172 + (
  3173 + CommonInfo cinfo,
  3174 + Actioner_Aspect aspect,
  3175 + Int32 n,
  3176 + ) =
  3177 + if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  3178 + [ "<script type=\"text/javascript\" language=\"JavaScript\">",
  3179 + " var lpust_",n," = new Array(); ",
  3180 + " lpust_",n,"[0] = 0; ",
  3181 + "</script>\n",
  3182 + "<a href=\"javascript:show_local_popup('lpu_",n,"','lpust_",n,"');\">",
  3183 + if aspect is
  3184 + {
  3185 + link(opt,text) then [text],
  3186 + push_button(opt, text) then [text],
  3187 + submit(opt, text) then [text],
  3188 + button(url_off,url_on) then
  3189 + [
  3190 + "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  3191 + " onMouseOver=\"this.src='",url_on,"'\" ",
  3192 + " onMouseOut=\"this.src='",url_off,"'\">",
  3193 + ],
  3194 +
  3195 + button(url_off,url_on,w,h) then
  3196 + [
  3197 + "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  3198 + " onMouseOver=\"this.src='",url_on,"'\" ",
  3199 + " onMouseOut=\"this.src='",url_off,"'\">",
  3200 + ],
  3201 +
  3202 + immediate_selector(name,size,choices) then alert,
  3203 + immediate_selector(name,size,choices,selected) then alert,
  3204 +
  3205 + },
  3206 + "</a>"].
  3207 +
  3208 +
  3209 +
  3210 +
  3211 +
  3212 + *** [5.5] Formating a private download link.
  3213 +
  3214 + We get the absolute path of the file to be downloaded, and the name under which it
  3215 + should appear to the client. The function 'format_private_download' creates an
  3216 + hypertext link for downloading the file. The secured mecanism of private download is
  3217 + used. This function is called by the function which formats HTML_Any($T) elements.
  3218 +
  3219 +
  3220 +define Printable_tree
  3221 + format_private_download
  3222 + (
  3223 + CommonInfo cinfo,
  3224 + String sn, // state name
  3225 + String abs_path, // absolute file path on server
  3226 + String name, // name of file as it appears in the browser
  3227 + String extra, // extra extension
  3228 + Maybe((String,List((String,String)))) action
  3229 +
  3230 + ) =
  3231 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3232 + with private_download_directory = site_directory+"/private_download",
  3233 + with auth = make_authorization(site_directory,secret,abs_path),
  3234 + [
  3235 + "<a href=\"",name,extra,"?zauth=",auth,
  3236 + if action is
  3237 + {
  3238 + failure then [ ]
  3239 + success(a) then if a is (an,args) then
  3240 + ["&amp;a=",an,format_extra_operands(args)]
  3241 + },
  3242 + "\">",
  3243 + name,
  3244 + "</a>"
  3245 + ].
  3246 +
  3247 +
  3248 +
  3249 +
  3250 + *** [5.6] Formating rows and cells in a table.
  3251 +
  3252 +define Int32
  3253 + percent
  3254 + (
  3255 + Int32 p
  3256 + ) =
  3257 + if p < 0 then 0 else if p > 100 then 100 else p.
  3258 +
  3259 +
  3260 +
  3261 +
  3262 + Formating cell options.
  3263 +
  3264 +
  3265 +
  3266 +define String
  3267 + format
  3268 + (
  3269 + BackgroundOption o
  3270 + ) =
  3271 + if o is
  3272 + {
  3273 + repeat then "",
  3274 + repeat_horizontal then "; background-repeat: repeat-x",
  3275 + repeat_vertical then "; background-repeat: repeat-y",
  3276 + no_repeat then "; background-repeat: no-repeat",
  3277 + center then "; background-position: center top"
  3278 + }.
  3279 +
  3280 +define String
  3281 + format
  3282 + (
  3283 + List(BackgroundOption) l
  3284 + ) =
  3285 + if l is
  3286 + {
  3287 + [ ] then "",
  3288 + [h . t] then format(h)+format(t)
  3289 + }.
  3290 +
  3291 +
  3292 +define String
  3293 + format
  3294 + (
  3295 + List(Cell_Option) options
  3296 + ) =
  3297 + if options is
  3298 + {
  3299 + [ ] then "",
  3300 + [h . t] then
  3301 + if h is
  3302 + {
  3303 + core_attrs(core_attr_list) then format_attrs(core_attr_list),
  3304 + left then " align=\"left\"",
  3305 + h_center then " align=\"center\"",
  3306 + right then " align=\"right\"",
  3307 + top then " valign=\"top\"",
  3308 + v_center then " valign=\"middle\"",
  3309 + bottom then " valign=\"bottom\"",
  3310 + base_line then " valign=\"baseline\"",
  3311 + background_color(c) then " bgcolor=\""+html_format(c)+"\"",
  3312 + background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",
  3313 + width(w) then " width=\""+w+"\"",
  3314 + percentage_width(n) then " width=\""+percent(n)+"%\"",
  3315 + height(h) then " height=\""+h+"\"",
  3316 + columns(n) then " colspan=\""+n+"\"",
  3317 + rows(n) then " rowspan=\""+n+"\"",
  3318 + nowrap then " nowrap"
  3319 + }
  3320 + + format(t)
  3321 + }.
  3322 +
  3323 +
  3324 + Normalizing a list of cell options (horizontal position must be specified; the default
  3325 + is 'left').
  3326 +
  3327 +define List(Cell_Option)
  3328 + normalize
  3329 + (
  3330 + List(Cell_Option) l
  3331 + ) =
  3332 + if member(l,left) then l else
  3333 + if member(l,h_center) then l else
  3334 + if member(l,right) then l else
  3335 + //[left . l]. CR: why ? there is no default because we can use CSS
  3336 + l.
  3337 +
  3338 +
  3339 + Formating cells in a row.
  3340 +
  3341 +define Printable_tree
  3342 + format
  3343 + (
  3344 + List(HTML_Cell($T)) cells,
  3345 + $T -> Printable_tree format_element
  3346 + ) =
  3347 + if cells is
  3348 + {
  3349 + [ ] then [ ],
  3350 + [h . t] then if h is cell(options,element) then
  3351 + ["<td ",format(reverse(normalize(options))),">",
  3352 + format_element(element),
  3353 + "</td>"
  3354 + . format(t,format_element)]
  3355 + }.
  3356 +
  3357 +define Printable_tree
  3358 + format
  3359 + (
  3360 + List(HTML_Header_Cell($T)) cells,
  3361 + $T -> Printable_tree format_element
  3362 + ) =
  3363 + if cells is
  3364 + {
  3365 + [ ] then [ ],
  3366 + [h . t] then if h is header_cell(options,element) then
  3367 + ["<th ",format(reverse(normalize(options))),">",
  3368 + format_element(element),
  3369 + "</th>"
  3370 + . format(t,format_element)]
  3371 + }.
  3372 +
  3373 + Formating the rows in a table.
  3374 +
  3375 +define Printable_tree
  3376 + format
  3377 + (
  3378 + List(HTML_Row($T)) rows,
  3379 + $T -> Printable_tree format_element,
  3380 + ) =
  3381 + if rows is
  3382 + {
  3383 + [ ] then [ ],
  3384 + [h . t] then if h is row(options,cells) then
  3385 + ["<tr ",format(reverse(options)),">",
  3386 + format(cells,format_element),
  3387 + "</tr>"
  3388 + . format(t,format_element)]
  3389 + }.
  3390 +
  3391 +define Printable_tree
  3392 + format
  3393 + (
  3394 + HTML_Header_Row($T) row,
  3395 + $T -> Printable_tree format_element
  3396 + ) =
  3397 + if row is
  3398 + {
  3399 + empty then [ ],
  3400 + header_row(options,cells) then
  3401 + ["<thead> <tr ",format(reverse(options)),">",
  3402 + format(cells,format_element),
  3403 + "</tr> </thead>"
  3404 + ]
  3405 + }.
  3406 +
  3407 +define Printable_tree
  3408 + format1
  3409 + (
  3410 + List(TextAreaOption) l
  3411 + ) =
  3412 + if l is
  3413 + {
  3414 + [] then [],
  3415 + [h . t] then if h is
  3416 + {
  3417 + disabled then [" disabled " . format1(t)]
  3418 + read_only then [" readonly " . format1(t)]
  3419 + wrap_lines then [" wrap " . format1(t)]
  3420 + }
  3421 + }.
  3422 +
  3423 +define Printable_tree
  3424 + format
  3425 + (
  3426 + List(TextAreaOption) l
  3427 + ) =
  3428 + if member(l,wrap_lines)
  3429 + then format1(l)
  3430 + else [" wrap=off " . format1(l)].
  3431 +
  3432 +
  3433 + *** [5.7] Formating elements which may be put anywhere.
  3434 +
  3435 + The function below involves the parameter $T which is later instantiated as
  3436 + 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions
  3437 + for these two types, and because formating of tables requires recursive calls of such
  3438 + functions, it is necessary to provide the 'format' function to be called recursively as
  3439 + an argument. Putting naively a call to 'format' will not work, because the compiler
  3440 + will look for a function able to format data of type $T (which is at that time distinct
  3441 + from any other type, including our two types). Such a function does not exist. Hence
  3442 + the function to be called for formating elements must be passed as a functional
  3443 + argument (called 'format_element' below). Actually, what we pass is a function taking
  3444 + a unique argument of type $T. Other informations (like the name of the state) are
  3445 + already in the function by way of full functionality.
  3446 +
  3447 +
  3448 + Formating text options. They are formated in CSS syntax, to be used within a
  3449 + 'style=...'.
  3450 +
  3451 +define String
  3452 + format
  3453 + (
  3454 + List(Text_Option) l
  3455 + ) =
  3456 + if l is
  3457 + {
  3458 + [ ] then "",
  3459 + [h . t] then if h is
  3460 + {
  3461 + size(n) then "font-size:"+n+"pt",
  3462 + font(fn) then "font-family:"+fn,
  3463 + color(c) then if c is rgb(r,g,b) then
  3464 + "color:rgb("+word8_to_int32(r)+","+word8_to_int32(g)+","+word8_to_int32(b)+")",
  3465 + italic then "font-style:italic",
  3466 + oblique then "font-style:oblique",
  3467 + small_capitals then "font-variant:small-caps",
  3468 + bold then "font-weight:bold",
  3469 + underlined then "text-decoration:underline",
  3470 + left_justified then "text-align:left",
  3471 + right_justified then "text-align:right",
  3472 + justified then "text-align:justify",
  3473 + line_through then "text-decoration:line-through",
  3474 + nowrap then "white-space:nowrap",
  3475 + class(class_name)then " class=\"" +class_name +"\"",
  3476 + id(id_name) then " id=\"" +id_name +"\""
  3477 + } + if t is [ ] then "" else ("; "+format(t))
  3478 + }.
  3479 +
  3480 +
  3481 +
  3482 + Formating table options.
  3483 +
  3484 +define String
  3485 + format
  3486 + (
  3487 + List(Table_Option) l,
  3488 + Bool border_seen
  3489 + ) =
  3490 + if l is
  3491 + {
  3492 + [ ] then if border_seen then "" else " border=\"0\" cellspacing=\"0\" cellpadding=\"0\"",
  3493 + [h . t] then if h is
  3494 + {
  3495 + background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),
  3496 + background_image(url) then " background="+url+format(t,border_seen),
  3497 + border(o,top,i,c) then " border=\""+o+"\" cellspacing=\""+top+"\" cellpadding=\""+i+"\""+
  3498 + //" bordercolor="+format(c)+
  3499 + format(t,true),
  3500 + width(w) then " width=\""+w+"\""+format(t,border_seen),
  3501 + percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),
  3502 + }
  3503 + }.
  3504 +
  3505 +
  3506 +
  3507 +
  3508 +
  3509 +define Printable_tree
  3510 + format_scroller
  3511 + (
  3512 + String sn,
  3513 + Int32 width,
  3514 + Int32 height,
  3515 + Int32 content_width,
  3516 + Int32 content_height,
  3517 + Int32 idnum, // identifying the scroller
  3518 + $T content,
  3519 + $T -> Printable_tree format_element
  3520 + ) =
  3521 + [
  3522 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  3523 + "function doscroll_",idnum,"(dx,dy) {\n",
  3524 + " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",
  3525 + " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",
  3526 + idnum,"').style\"); } else\n",
  3527 + " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",
  3528 + " var x_",idnum," = parseInt(c_",idnum,".left);\n",
  3529 + " var y_",idnum," = parseInt(c_",idnum,".top);\n",
  3530 + " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",
  3531 + " { x_",idnum," += dx; }\n",
  3532 + " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",
  3533 + " { y_",idnum," += dy; }\n",
  3534 + " c_",idnum,".left = x_",idnum,";\n",
  3535 + " c_",idnum,".top = y_",idnum,";\n",
  3536 + " }\n",
  3537 + "</script>\n",
  3538 + "<table>",
  3539 + "<tbody>",
  3540 + "<tr>",
  3541 + "<td align=left valign=top",
  3542 + " width=",width,
  3543 + " height=",height,
  3544 + ">",
  3545 + "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",
  3546 + " clip:rect(0px ",width,"px ",height,"px 0px)\">",
  3547 + "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",
  3548 + format_element(content),
  3549 + "</div>",
  3550 + "</div>",
  3551 + "</td>",
  3552 + "<td valign=bottom>",
  3553 + "<table>",
  3554 + "<tbody>",
  3555 + "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onmousedown=\"doscroll_",
  3556 + idnum,"(0,20);\"></td></tr>",
  3557 + "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onmousedown=\"doscroll_",
  3558 + idnum,"(0,-20);\"></td></tr>",
  3559 + "</tbody>",
  3560 + "</table>",
  3561 + "</td>",
  3562 + "</tr>",
  3563 + (if content_width > width then
  3564 + [
  3565 + "<tr>",
  3566 + "<td align=right>",
  3567 + "<table>",
  3568 + "<tbody>",
  3569 + "<tr>",
  3570 + "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onmousedown=\"doscroll_",
  3571 + idnum,"(20,0);\"></td>",
  3572 + "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onmousedown=\"doscroll_",
  3573 + idnum,"(-20,0);\"></td>",
  3574 + "</tr>",
  3575 + "</tbody>",
  3576 + "</table>",
  3577 + "</td>",
  3578 + "</tr>",
  3579 + ] else [ ]),
  3580 + "</tbody>",
  3581 + "</table>"
  3582 + ].
  3583 +
  3584 +
  3585 +/*
  3586 + define Printable_tree
  3587 + popup_topbar
  3588 + (
  3589 + CommonInfo cinfo,
  3590 + String title,
  3591 + RGB color,
  3592 + Int32 width,
  3593 + ) =
  3594 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3595 + with xxxx = web_arg_encode(sha1((title,color,width))),
  3596 + path = site_directory+"/public/buttons/t"+xxxx+".jpg",
  3597 + result = (Printable_tree)["<img alt=\"button\" src=\"buttons/t"+xxxx+".jpg\">"],
  3598 + if file_exists(path) then result else
  3599 + with col = to_rgba(color),
  3600 + bg = create_button_background(col,width,20),
  3601 + very_light_color = lighten(col,70),
  3602 + dark_color = darken(col,40),
  3603 + title_width = printed_text_width(font,title),
  3604 + draw_button_text(bg,title,title_width,very_light_color,dark_color,font);
  3605 + forget(write_image_to_JPEG_file(to_JPEG(bg),path,100));
  3606 + result.
  3607 +
  3608 +
  3609 + define Printable_tree
  3610 + popup_close_button
  3611 + (
  3612 + CommonInfo cinfo,
  3613 + RGB color,
  3614 + String div_name,
  3615 + String state_var_name,
  3616 + ) =
  3617 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3618 + with xxxx = web_arg_encode(sha1(color)),
  3619 + path_on = site_directory+"/public/buttons/c"+xxxx+"_on.jpg",
  3620 + path_off = site_directory+"/public/buttons/c"+xxxx+"_off.jpg",
  3621 + result = (Printable_tree)["<img alt=\"button\" src=\"buttons/c"+xxxx+"_off.jpg\"",
  3622 + " onMouseOver=\"this.src='buttons/c"+xxxx+"_on.jpg'\"",
  3623 + " onMouseOut=\"this.src='buttons/c"+xxxx+"_off.jpg'\"",
  3624 + " onMouseDown=\"show_local_popup('",div_name,"','",state_var_name,"')\">"],
  3625 + if file_exists(path_on) then result else
  3626 + with col = to_rgba(color),
  3627 + title = "x",
  3628 + title_width = printed_text_width(font,title),
  3629 + bg_on = create_button_background(lighten(col,30),20,20),
  3630 + bg_off = create_button_background(col,20,20),
  3631 + very_light_color = lighten(col,70),
  3632 + dark_color = darken(col,40),
  3633 + draw_button_text(bg_on,title,title_width,very_light_color,dark_color,font);
  3634 + draw_button_text(bg_off,title,title_width,very_light_color,dark_color,font);
  3635 + forget(write_image_to_JPEG_file(to_JPEG(bg_on),path_on,100));
  3636 + forget(write_image_to_JPEG_file(to_JPEG(bg_off),path_off,100));
  3637 + result.
  3638 +
  3639 +
  3640 +*/
  3641 +
  3642 +
  3643 + // The function below formats a datum of type 'HTML_Any($T)'.
  3644 +
  3645 +define Printable_tree
  3646 + format
  3647 + (
  3648 + CommonInfo cinfo,
  3649 + String sn, // state_name
  3650 + Var(Int32) ic_v,
  3651 + HTML_Any($T) element,
  3652 + $T -> Printable_tree format_element, // able to format a datum of type $T
  3653 + Bool is_https,
  3654 + ) =
  3655 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3656 + if element is
  3657 + {
  3658 + any_text(opts,t) then
  3659 + ["<span ", format_text_options(opts), ">",t,"</span>"],
  3660 + any_preformated(opts,s) then
  3661 + ["<span ", format_text_options(opts), "><pre>",s,"</pre></span>"],
  3662 + //["<pre>",s,"</pre>"],
  3663 + any_paragraph(opts,e) then
  3664 + ["<p ", format_text_options(opts), ">",format_element(e),"</p>\n"],
  3665 + any_image(url) then
  3666 + ["<img alt=\"",url,"\" src=\"",url,"\">"],
  3667 + any_image(url,w,h) then
  3668 + ["<img alt=\"",url,"\" src=\"",url,"\" width=",w," height=",h," />"],
  3669 + any_table(opts,h_row, rows) then
  3670 + ["<table ",format(reverse(opts),false),">",
  3671 + format(h_row,format_element),
  3672 + "<tbody>",format(rows,format_element),"</tbody></table>\n"],
  3673 + any_center(e) then
  3674 + ["<center>",format_element(e),"</center>"],
  3675 + any_mail_to(email,elem) then
  3676 + ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],
  3677 + any_scroller(w,h,cw,ch,c) then
  3678 + format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),
  3679 + any_fixed_size(w,h,c) then
  3680 + with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),
  3681 + ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",
  3682 + "secondary document",
  3683 + "</object>"],
  3684 + any_fixed_size_2(w,h,fn) then
  3685 + with url = fn+"?zauth="+make_authorization(site_directory,secret,
  3686 + fn),
  3687 + ["<object data=\"",url,"\" type=\"text/html\" width=\"",format(w),"\" height=\"",format(h),"\" >",
  3688 + "secondary document",
  3689 + "</object>"],
  3690 + any_actioner(c,t,a,an,eo,ja,fn) then
  3691 + format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https),
  3692 + any_foreign_link(options,url) then
  3693 + ["<a href=\"",url,"\" ", format_text_options(options), " />"],
  3694 + any_foreign_link(options,url,name) then
  3695 + ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],
  3696 + any_private_download(url,name,extra_ext,action) then
  3697 + format_private_download(cinfo,sn,url,name,extra_ext,action),
  3698 + any_div(options, e) then
  3699 + [format_div_option(options), format_element(e),"</div>\n"],
  3700 + any_div_empty(options) then
  3701 + [format_div_option(options), "</div>\n"],
  3702 + any_coreattrs(attributs) then
  3703 + [format_attrs(attributs)]
  3704 + }.
  3705 +
  3706 +
  3707 +
  3708 +
  3709 + // *** [5.8] Formating 'in form' elements.
  3710 +
  3711 +
  3712 +
  3713 +
  3714 +
  3715 +define Printable_tree
  3716 + format
  3717 + (
  3718 + CommonInfo cinfo,
  3719 + String fn, // form_name
  3720 + String sn, // state_name
  3721 + Var(Int32) ic_v, // idnum counter variable
  3722 + HTML_In_Form element,
  3723 + Bool is_https,
  3724 + ) =
  3725 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3726 + with format_element = (HTML_In_Form e) |-> format(cinfo,fn,sn,ic_v,e,is_https),
  3727 + if element is
  3728 + {
  3729 + literal_pt(t) then t,
  3730 + literal(t) then [t],
  3731 + sequence(l) then flat(map(format_element,l))
  3732 + text(opts,t) then
  3733 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
  3734 + preformated(o,s) then
  3735 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
  3736 + paragraph(opts,t) then
  3737 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
  3738 + image(url) then
  3739 + format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
  3740 + image(url,w,h) then
  3741 + format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
  3742 + table(opts,header_row,rows) then
  3743 + format(cinfo,sn,ic_v,any_table(opts, header_row, rows),format_element,is_https),
  3744 + center(e) then
  3745 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
  3746 + mail_to(a,e) then
  3747 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
  3748 + scroller(w,h,cw,ch,c) then
  3749 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
  3750 + actioner(c,t,a,an,eo,ja) then
  3751 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
  3752 + foreign_link(options,url) then
  3753 + format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https),
  3754 + foreign_link(options,url,name) then
  3755 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
  3756 + private_download(url,name,extra,action) then
  3757 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
  3758 + text_input(options, label_text, id, name, i, w) then
  3759 + [ "<label for=\"",id,"\">",label_text,"</label>",
  3760 + "<input type=\"text\" name=\"o",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
  3761 + text_input_ro(options, label_text, id, name,i,w) then
  3762 + [ "<label for=\"",id,"\">",label_text,"</label>",
  3763 + "<input readonly=\"readonly\" type=\"text\" name=\"o",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
  3764 + password_input(options, label_text, id, name,i,w) then
  3765 + [ "<label for=\"",id,"\">",label_text,"</label>",
  3766 + "<input type=\"password\" name=\"p",name,"\" id=\"",id,"\" size=\"",w,"\" value=\"",i,"\"",format_attrs(options)," />"],
  3767 + text_area(opts,n,i,w,h) then
  3768 + ["<textarea ",format(opts)," name=\"o",n,"\" cols=\"",w,"\" rows=\"",h,"\">",i,"</textarea>"],
  3769 + file_upload(label, id, n, w) then
  3770 + ["<label for=\"",id,"\">",label,"</label>",
  3771 + "<input type=\"file\" id=\"",id,"\" size=\"",w,"\" name=\"o",n,"\" />"],
  3772 + selector(opts, label, id, n,s,cs) then
  3773 + ["<label for=\"",id,"\">",label,"</label>",
  3774 + "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],
  3775 + selector(opts, label, id, n,s,cs,sd) then
  3776 + ["<label for=\"",id,"\">",label,"</label>",
  3777 + "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],
  3778 + selector_c(opts, label, id, n,s,cs) then
  3779 + ["<label for=\"",id,"\">",label,"</label>",
  3780 + "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs),"</select>"],
  3781 + selector_c(opts, label, id, n,s,cs,sd) then
  3782 + ["<label for=\"",id,"\">",label,"</label>",
  3783 + "<select id=\"",id,"\" name=\"o",n,"\" size=\"",s,"\" ",format_attrs(opts),">",format_choices(cs,sd),"</select>"],
  3784 +
  3785 + radio_button(options, label_text, id, n, v, c) then
  3786 + [ "<label for=\"",id,"\">",label_text,"</label>",
  3787 + "<input type=\"radio\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />"],
  3788 + radio_button_r(options, label_text, id, n, v, c) then
  3789 + [ "<input type=\"radio\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\"" else ""),format_attrs(options)," />",
  3790 + "<label for=\"",id,"\">",label_text,"</label>"],
  3791 + check_box(options, label_text, id, n, v, c) then
  3792 + [ if length(label_text) > 0 then ["<label for=\"",id,"\">",label_text,"</label>"] else [""],
  3793 + "<input type=\"checkbox\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />"]
  3794 + check_box_r(options, label_text, id, n, v, c) then
  3795 + [ "<input type=\"checkbox\" name=\"o",n,"\" id=\"",id,"\" value=\"",v,"\"",(if c then " checked=\"checked\" " else ""),format_attrs(options)," />",
  3796 + if length(label_text) > 0 then ["<label for=\"",id,"\">",label_text,"</label>"] else [""] ]
  3797 + div(options, e) then
  3798 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
  3799 + div_empty(options) then
  3800 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
  3801 + hidden(name, value) then
  3802 + ["<input type=\"hidden\" name=\"o",name,"\" value=\"",value,"\" />"],
  3803 +
  3804 + }.
  3805 +
  3806 +
  3807 +
  3808 +
  3809 +
  3810 +
  3811 + *** [5.9] Formating 'off form' elements.
  3812 +
  3813 + The encryption type 'multipart/form-data' is required for a form containing an upload.
  3814 +
  3815 +
  3816 +define Bool
  3817 + contains_an_upload
  3818 + (
  3819 + HTML_In_Form form_content
  3820 + ).
  3821 +
  3822 +define Bool
  3823 + contains_an_upload
  3824 + (
  3825 + HTML_Row(HTML_In_Form) row
  3826 + ) =
  3827 + mapor(contains_an_upload,
  3828 + map(content,cells(row))).
  3829 +
  3830 +
  3831 +define Bool
  3832 + contains_an_upload
  3833 + (
  3834 + HTML_In_Form form_content
  3835 + ) =
  3836 + if form_content is
  3837 + {
  3838 + literal_pt(t) then false,
  3839 + literal(t) then false,
  3840 + sequence(l) then mapor(contains_an_upload,l)
  3841 + text(o,t) then false,
  3842 + preformated(o,s) then false,
  3843 + paragraph(o,t) then false,
  3844 + image(u) then false,
  3845 + image(u,w,h) then false,
  3846 + table(o,_,rows) then mapor(contains_an_upload,rows),
  3847 + center(e) then contains_an_upload(e),
  3848 + mail_to(m,e) then false, // 'e' may but should not contain an upload
  3849 + scroller(w,h,cw,ch,e) then contains_an_upload(e),
  3850 + actioner(c,t,a,an,eo,ja) then false,
  3851 + foreign_link(o,u) then false,
  3852 + foreign_link(o,u,n) then false,
  3853 + private_download(p,n,e,a) then false,
  3854 + text_input(o,lt,l,n,i,w) then false,
  3855 + text_input_ro(o,lt,l,n,i,w) then false,
  3856 + password_input(o,lt,l,n,i,w) then false,
  3857 + text_area(o,n,i,w,h) then false,
  3858 + file_upload(l,i,n,w) then true,
  3859 + selector(o,l,i,n,s,c) then false,
  3860 + selector(o,l,i,n,s,c,p) then false,
  3861 + selector_c(o,l,i,n,s,c) then false,
  3862 + selector_c(o,l,i,n,s,c,p) then false,
  3863 + radio_button(o,_,_,n,v,c) then false,
  3864 + radio_button_r(o,_,_,n,v,c) then false,
  3865 + check_box(o,_,_,n,v,c) then false,
  3866 + check_box_r(o,_,_,n,v,c) then false,
  3867 + div(o,c) then contains_an_upload(c),
  3868 + div_empty(o) then false,
  3869 + hidden(_,_) then false
  3870 + }.
  3871 +
  3872 +define String
  3873 + enctype
  3874 + (
  3875 + HTML_In_Form form_content
  3876 + ) =
  3877 + if contains_an_upload(form_content)
  3878 + then " enctype=\"multipart/form-data\""
  3879 + else "".
  3880 +
  3881 +
  3882 +
  3883 +define Printable_tree
  3884 + format
  3885 + (
  3886 + CommonInfo cinfo,
  3887 + String sn, // state_name
  3888 + Var(Int32) ic_v, // 'idnum' counter variable
  3889 + HTML_Off_Form element,
  3890 + Bool is_https,
  3891 + ) =
  3892 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3893 + with format_element = (HTML_Off_Form e) |-> format(cinfo,sn,ic_v,e,is_https),
  3894 + if element is
  3895 + {
  3896 + literal_pt(t) then t,
  3897 + literal(t) then [t],
  3898 + sequence(l) then flat(map(format_element,l)),
  3899 + text(opts,t) then
  3900 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
  3901 + preformated(o,s) then
  3902 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
  3903 + paragraph(opts,t) then
  3904 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
  3905 + image(url) then
  3906 + format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
  3907 + image(url,w,h) then
  3908 + format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
  3909 + table(opts,header_row, rows) then
  3910 + format(cinfo,sn,ic_v,any_table(opts,header_row, rows),format_element,is_https),
  3911 + center(e) then
  3912 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
  3913 + mail_to(a,e) then
  3914 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
  3915 + scroller(w,h,cw,ch,c) then
  3916 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
  3917 + fixed_size(w,h,c) then
  3918 + format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https),
  3919 + fixed_size_2(w,h,fn) then
  3920 + format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https),
  3921 + actioner(c,t,a,an,eo,ja) then
  3922 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https),
  3923 + actioner(c,t,a,an,eo,ja,fn) then
  3924 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
  3925 + foreign_link(options,url) then
  3926 + format(cinfo,sn,ic_v,any_foreign_link(options,url),format_element,is_https),
  3927 + foreign_link(options,url,name) then
  3928 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
  3929 + private_download(url,name,extra,action) then
  3930 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
  3931 + label(n) then ["<a name=\"",n,"\">"],
  3932 + form(fn,attributs, c) then
  3933 + [
  3934 + "<form name=\"f",fn,"\"",
  3935 + format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),
  3936 + " method=\"post\"",
  3937 + enctype(c),
  3938 + " action=\"http",
  3939 + if is_https then "s" else "",
  3940 + "://",common_name,":",http_port,"/\">",
  3941 + // action is set dynamically by
  3942 + // the actioner using JavaScript
  3943 + format(cinfo,fn,sn,ic_v,c,is_https),
  3944 + "</form>"
  3945 + ]
  3946 + form(fn,attributs, action_name, extra_ops, c) then
  3947 + with url = make_actioner_url(cinfo, same, same,
  3948 + sn, action_name, extra_ops, is_https),
  3949 + [
  3950 + "<form name=\"f",fn,"\"",
  3951 + format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),
  3952 + " method=\"post\"",
  3953 + enctype(c),
  3954 + " action=\"" + url + "\">",
  3955 +// " action=\"http",
  3956 +// if is_https then "s" else "",
  3957 +// "://",common_name,":",http_port,"/\">",
  3958 + // action is set dynamically by
  3959 + // the actioner using JavaScript
  3960 + format(cinfo,fn,sn,ic_v,c,is_https),
  3961 + "</form>"
  3962 + ]
  3963 + div(options, e) then
  3964 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
  3965 + div_empty(options) then
  3966 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
  3967 +
  3968 + }.
  3969 +
  3970 +
  3971 +
  3972 +
  3973 + *** [5.10] Formating meta-tags.
  3974 +
  3975 +define Printable_tree
  3976 + format_keywords
  3977 + (
  3978 + List(String) l
  3979 + ) =
  3980 + if l is
  3981 + {
  3982 + [] then [ ],
  3983 + [h . t] then if t is []
  3984 + then [h]
  3985 + else [h , ", " . format_keywords(t)]
  3986 + }.
  3987 +
  3988 +
  3989 +define Printable_tree
  3990 + format
  3991 + (
  3992 + CommonInfo cinfo,
  3993 + String state_name,
  3994 + HTML_Meta m,
  3995 + Bool is_https
  3996 + ) =
  3997 + if m is
  3998 + {
  3999 + keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\" />\n"],
  4000 + refresh(co,ta,an,delay) then
  4001 + ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",
  4002 + make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\" />\n"],
  4003 + meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\" />\n"],
  4004 + http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\" />\n"],
  4005 + generic_meta(l) then ["<meta ",
  4006 + flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
  4007 + l)),
  4008 + " />\n"],
  4009 + literal(s) then [s]
  4010 + }.
  4011 +
  4012 +
  4013 +define Printable_tree
  4014 + format
  4015 + (
  4016 + CommonInfo cinfo,
  4017 + String state_name,
  4018 + List(HTML_Meta) metas,
  4019 + Bool is_https,
  4020 + String charset
  4021 + ) =
  4022 + if metas is
  4023 + {
  4024 + [] then [format(cinfo,state_name,http_equiv("content-type",
  4025 + "text/html; charset="+charset),is_https)],
  4026 + [h . t] then [format(cinfo,state_name,h,is_https)
  4027 + . format(cinfo,state_name,t,is_https,charset)]
  4028 + }.
  4029 +
  4030 +
  4031 +define Printable_tree
  4032 + format
  4033 + (
  4034 + Body_Option o
  4035 + ) =
  4036 + if o is
  4037 + {
  4038 + background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],
  4039 + background_image(n) then [" background=", n],
  4040 + background_image(n,o) then [" style=\"background: url(",n,")",format(o),"\""]
  4041 +
  4042 + }.
  4043 +
  4044 +
  4045 +
  4046 +define Printable_tree
  4047 + format
  4048 + (
  4049 + List(Body_Option) l
  4050 + ) =
  4051 + if l is
  4052 + {
  4053 + [ ] then [ ],
  4054 + [h . t] then [format(h) . format(t)]
  4055 + }.
  4056 +
  4057 +define Printable_tree
  4058 + add_css_files
  4059 + (
  4060 + List(CSS_File) l
  4061 + ) =
  4062 + if l is
  4063 + {
  4064 + [ ] then [ ],
  4065 + [h . t] then
  4066 + [ ["<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file_name(h) + "\" />\n" ]
  4067 + . add_css_files(t)]
  4068 + }.
  4069 +
  4070 +define Printable_tree
  4071 + add_js_files
  4072 + (
  4073 + List(JS_File) l
  4074 + ) =
  4075 + if l is
  4076 + {
  4077 + [ ] then [ ],
  4078 + [h . t] then
  4079 + [ ["<script src=\""+ file_name(h) +"\" type=\"text/javascript\"></script>\n" ]
  4080 + . add_js_files(t)]
  4081 + }.
  4082 +
  4083 +define Printable_tree
  4084 + add_css_styles
  4085 + (
  4086 + List(CSS_Style) css_styles
  4087 + ) =
  4088 +
  4089 + if css_styles is
  4090 + {
  4091 + [] then [],
  4092 + [_._] then [ "<style type=\"text/css\"><!--\n",
  4093 + format_css_styles(css_styles),
  4094 + " --></style>\n"
  4095 + ]
  4096 + }.
  4097 +
  4098 +define Printable_tree
  4099 + format
  4100 + (
  4101 + CommonInfo cinfo,
  4102 + String state_name,
  4103 + HTML_Page page,
  4104 + Bool is_https,
  4105 + String charset
  4106 + ) =
  4107 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  4108 + with ic_v = var((Int32)0),
  4109 + if page is
  4110 + {
  4111 + html_page(title,metas,css_styles, css_files, js_files, body) then
  4112 + if body is body(options,element) then
  4113 + [ doctype_w3c_header,
  4114 + "<html>\n",
  4115 + "<head>\n",
  4116 + add_css_styles(css_styles),
  4117 + add_css_files(css_files),
  4118 + add_js_files(js_files),
  4119 + "<link rel=\"shortcut icon\" href=\"favicon.ico\" />\n",
  4120 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  4121 + " function show_local_popup(divname,stvname) {",
  4122 + " if (document.layers) { var d = eval(document.divname); } else\n",
  4123 + " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",
  4124 + " if (document.all) { var d = eval(document.all.divname.style)};\n",
  4125 + // " alert(typeof(eval(stvname))); ",
  4126 + " var s = eval(stvname); ",
  4127 + " if (s[0]==0) ",
  4128 + " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",
  4129 + " { s[0]=0; d.style.visibility = 'hidden'; }; }",
  4130 + "</script>\n",
  4131 + "<title>",title,"</title>\n", // put title
  4132 + format(cinfo,state_name,metas,is_https,charset), // format the metas
  4133 + "</head>\n",
  4134 + "<body ", format(options), ">", // format body options
  4135 + //"<center>",
  4136 + format(cinfo,state_name,ic_v,element,is_https),
  4137 + //"</center>",
  4138 + "</body>\n",
  4139 + "</html>"
  4140 + ]
  4141 + }.
  4142 +
  4143 +
  4144 +
  4145 +
  4146 +
  4147 +